handle_import_and_package.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "toolchain/base/value_store.h"
  5. #include "toolchain/lex/tokenized_buffer.h"
  6. #include "toolchain/parse/context.h"
  7. #include "toolchain/parse/node_ids.h"
  8. #include "toolchain/parse/node_kind.h"
  9. namespace Carbon::Parse {
  10. // Provides common error exiting logic that skips to the semi, if present.
  11. static auto OnParseError(Context& context, Context::StateStackEntry state,
  12. NodeKind directive) -> void {
  13. return context.AddNode(directive, context.SkipPastLikelyEnd(state.token),
  14. state.subtree_start, /*has_error=*/true);
  15. }
  16. // Handles parsing of the library name. Returns the name's ID on success, which
  17. // may be invalid for `default`.
  18. static auto HandleLibraryName(Context& context, bool accept_default)
  19. -> std::optional<StringLiteralValueId> {
  20. if (auto library_name_token =
  21. context.ConsumeIf(Lex::TokenKind::StringLiteral)) {
  22. context.AddLeafNode(NodeKind::LibraryName, *library_name_token);
  23. return context.tokens().GetStringLiteralValue(*library_name_token);
  24. }
  25. if (accept_default) {
  26. if (auto default_token = context.ConsumeIf(Lex::TokenKind::Default)) {
  27. context.AddLeafNode(NodeKind::DefaultLibrary, *default_token);
  28. return StringLiteralValueId::Invalid;
  29. }
  30. }
  31. CARBON_DIAGNOSTIC(
  32. ExpectedLibraryNameOrDefault, Error,
  33. "Expected `default` or a string literal to specify the library name.");
  34. CARBON_DIAGNOSTIC(ExpectedLibraryName, Error,
  35. "Expected a string literal to specify the library name.");
  36. context.emitter().Emit(*context.position(), accept_default
  37. ? ExpectedLibraryNameOrDefault
  38. : ExpectedLibraryName);
  39. return std::nullopt;
  40. }
  41. // Returns whether `api` or `impl` is provided, or prints an error and returns
  42. // nullopt.
  43. static auto HandleApiOrImpl(Context& context)
  44. -> std::optional<Tree::ApiOrImpl> {
  45. switch (context.PositionKind()) {
  46. case Lex::TokenKind::Api: {
  47. context.AddLeafNode(NodeKind::PackageApi,
  48. context.ConsumeChecked(Lex::TokenKind::Api));
  49. return Tree::ApiOrImpl::Api;
  50. break;
  51. }
  52. case Lex::TokenKind::Impl: {
  53. context.AddLeafNode(NodeKind::PackageImpl,
  54. context.ConsumeChecked(Lex::TokenKind::Impl));
  55. return Tree::ApiOrImpl::Impl;
  56. break;
  57. }
  58. default: {
  59. CARBON_DIAGNOSTIC(ExpectedApiOrImpl, Error, "Expected `api` or `impl`.");
  60. context.emitter().Emit(*context.position(), ExpectedApiOrImpl);
  61. return std::nullopt;
  62. }
  63. }
  64. }
  65. // Handles everything after the directive's introducer.
  66. static auto HandleDirectiveContent(Context& context,
  67. Context::StateStackEntry state,
  68. NodeKind directive, bool is_export,
  69. llvm::function_ref<void()> on_parse_error)
  70. -> void {
  71. Tree::PackagingNames names{
  72. .node_id = ImportDirectiveId(NodeId(state.subtree_start)),
  73. .is_export = is_export};
  74. if (directive != NodeKind::LibraryDirective) {
  75. if (auto package_name_token =
  76. context.ConsumeIf(Lex::TokenKind::Identifier)) {
  77. if (names.is_export) {
  78. names.is_export = false;
  79. state.has_error = true;
  80. CARBON_DIAGNOSTIC(ExportImportPackage, Error,
  81. "`export` cannot be used when importing a package.");
  82. context.emitter().Emit(*package_name_token, ExportImportPackage);
  83. }
  84. names.package_id = context.tokens().GetIdentifier(*package_name_token);
  85. context.AddLeafNode(NodeKind::PackageName, *package_name_token);
  86. } else if (directive == NodeKind::PackageDirective ||
  87. !context.PositionIs(Lex::TokenKind::Library)) {
  88. CARBON_DIAGNOSTIC(ExpectedIdentifierAfterPackage, Error,
  89. "Expected identifier after `package`.");
  90. CARBON_DIAGNOSTIC(ExpectedIdentifierAfterImport, Error,
  91. "Expected identifier or `library` after `import`.");
  92. context.emitter().Emit(*context.position(),
  93. directive == NodeKind::PackageDirective
  94. ? ExpectedIdentifierAfterPackage
  95. : ExpectedIdentifierAfterImport);
  96. on_parse_error();
  97. return;
  98. }
  99. }
  100. // Parse the optional library keyword.
  101. bool accept_default = !names.package_id.is_valid();
  102. if (directive == NodeKind::LibraryDirective) {
  103. auto library_id = HandleLibraryName(context, accept_default);
  104. if (!library_id) {
  105. on_parse_error();
  106. return;
  107. }
  108. names.library_id = *library_id;
  109. } else {
  110. auto next_kind = context.PositionKind();
  111. if (next_kind == Lex::TokenKind::Library) {
  112. auto library_token = context.ConsumeChecked(Lex::TokenKind::Library);
  113. auto library_subtree_start = context.tree().size();
  114. auto library_id = HandleLibraryName(context, accept_default);
  115. if (!library_id) {
  116. on_parse_error();
  117. return;
  118. }
  119. names.library_id = *library_id;
  120. context.AddNode(NodeKind::LibrarySpecifier, library_token,
  121. library_subtree_start,
  122. /*has_error=*/false);
  123. } else if (next_kind == Lex::TokenKind::StringLiteral ||
  124. (accept_default && next_kind == Lex::TokenKind::Default)) {
  125. // If we come across a string literal and we didn't parse `library
  126. // "..."` yet, then most probably the user forgot to add `library`
  127. // before the library name.
  128. CARBON_DIAGNOSTIC(MissingLibraryKeyword, Error,
  129. "Missing `library` keyword.");
  130. context.emitter().Emit(*context.position(), MissingLibraryKeyword);
  131. on_parse_error();
  132. return;
  133. }
  134. }
  135. std::optional<Tree::ApiOrImpl> api_or_impl;
  136. if (directive != NodeKind::ImportDirective) {
  137. api_or_impl = HandleApiOrImpl(context);
  138. if (!api_or_impl) {
  139. on_parse_error();
  140. return;
  141. }
  142. }
  143. if (auto semi = context.ConsumeIf(Lex::TokenKind::Semi)) {
  144. if (directive == NodeKind::ImportDirective) {
  145. context.AddImport(names);
  146. } else {
  147. context.set_packaging_directive(names, *api_or_impl);
  148. }
  149. context.AddNode(directive, *semi, state.subtree_start, state.has_error);
  150. } else {
  151. context.DiagnoseExpectedDeclSemi(context.tokens().GetKind(state.token));
  152. on_parse_error();
  153. }
  154. }
  155. // Returns true if currently in a valid state for imports, false otherwise. May
  156. // update the packaging state respectively.
  157. static auto VerifyInImports(Context& context, Lex::TokenIndex intro_token)
  158. -> bool {
  159. switch (context.packaging_state()) {
  160. case Context::PackagingState::FileStart:
  161. // `package` is no longer allowed, but `import` may repeat.
  162. context.set_packaging_state(Context::PackagingState::InImports);
  163. return true;
  164. case Context::PackagingState::InImports:
  165. return true;
  166. case Context::PackagingState::AfterNonPackagingDecl: {
  167. context.set_packaging_state(
  168. Context::PackagingState::InImportsAfterNonPackagingDecl);
  169. CARBON_DIAGNOSTIC(ImportTooLate, Error,
  170. "`import` and `export` directives must come after the "
  171. "`package` directive (if present) and before any other "
  172. "entities in the file.");
  173. CARBON_DIAGNOSTIC(FirstDecl, Note, "First declaration is here.");
  174. context.emitter()
  175. .Build(intro_token, ImportTooLate)
  176. .Note(context.first_non_packaging_token(), FirstDecl)
  177. .Emit();
  178. return false;
  179. }
  180. case Context::PackagingState::InImportsAfterNonPackagingDecl:
  181. // There is a sequential block of misplaced `import` statements, which can
  182. // occur if a declaration is added above `import`s. Avoid duplicate
  183. // warnings.
  184. return false;
  185. }
  186. }
  187. // Common logic for both `import` and `export import`, distinguished by whether
  188. // `export_token` is valid.
  189. static auto HandleImportHelper(Context& context,
  190. const Context::StateStackEntry& state,
  191. Lex::TokenIndex export_token) -> void {
  192. auto directive = NodeKind::ImportDirective;
  193. auto on_parse_error = [&] { OnParseError(context, state, directive); };
  194. auto intro_token = context.ConsumeChecked(Lex::TokenKind::Import);
  195. context.AddLeafNode(NodeKind::ImportIntroducer, intro_token);
  196. if (export_token.is_valid()) {
  197. context.AddLeafNode(NodeKind::ImportExport, export_token);
  198. }
  199. if (VerifyInImports(context, intro_token)) {
  200. HandleDirectiveContent(context, state, directive, export_token.is_valid(),
  201. on_parse_error);
  202. } else {
  203. on_parse_error();
  204. }
  205. }
  206. auto HandleExport(Context& context) -> void {
  207. auto state = context.PopState();
  208. context.ConsumeChecked(Lex::TokenKind::Export);
  209. // Error for both Main//default and every implementation file.
  210. auto packaging = context.tree().packaging_directive();
  211. if (!packaging || packaging->api_or_impl == Tree::ApiOrImpl::Impl) {
  212. CARBON_DIAGNOSTIC(ExportFromImpl, Error,
  213. "`export` is only allowed in API files.");
  214. context.emitter().Emit(state.token, ExportFromImpl);
  215. state.has_error = true;
  216. }
  217. if (context.PositionIs(Lex::TokenKind::Import)) {
  218. HandleImportHelper(context, state, state.token);
  219. } else {
  220. if (!VerifyInImports(context, state.token)) {
  221. state.has_error = true;
  222. }
  223. context.AddLeafNode(NodeKind::ExportIntroducer, state.token,
  224. state.has_error);
  225. context.PushState(state, State::ExportFinish);
  226. context.PushState(State::DeclNameAndParamsAsNone, state.token);
  227. }
  228. }
  229. auto HandleExportFinish(Context& context) -> void {
  230. auto state = context.PopState();
  231. context.AddNodeExpectingDeclSemi(state, NodeKind::ExportDirective,
  232. Lex::TokenKind::Export,
  233. /*is_def_allowed=*/false);
  234. }
  235. auto HandleImport(Context& context) -> void {
  236. auto state = context.PopState();
  237. HandleImportHelper(context, state, /*export_token=*/Lex::TokenIndex::Invalid);
  238. }
  239. // Handles common logic for `package` and `library`.
  240. static auto HandlePackageAndLibraryDirectives(Context& context,
  241. Lex::TokenKind intro_token_kind,
  242. NodeKind intro,
  243. NodeKind directive) -> void {
  244. auto state = context.PopState();
  245. auto on_parse_error = [&] { OnParseError(context, state, directive); };
  246. auto intro_token = context.ConsumeChecked(intro_token_kind);
  247. context.AddLeafNode(intro, intro_token);
  248. if (intro_token != Lex::TokenIndex::FirstNonCommentToken) {
  249. CARBON_DIAGNOSTIC(PackageTooLate, Error,
  250. "The `{0}` directive must be the first non-comment line.",
  251. Lex::TokenKind);
  252. CARBON_DIAGNOSTIC(FirstNonCommentLine, Note,
  253. "First non-comment line is here.");
  254. context.emitter()
  255. .Build(intro_token, PackageTooLate, intro_token_kind)
  256. .Note(Lex::TokenIndex::FirstNonCommentToken, FirstNonCommentLine)
  257. .Emit();
  258. on_parse_error();
  259. return;
  260. }
  261. // `package`/`library` is no longer allowed, but `import` may repeat.
  262. context.set_packaging_state(Context::PackagingState::InImports);
  263. HandleDirectiveContent(context, state, directive, /*is_export=*/false,
  264. on_parse_error);
  265. }
  266. auto HandlePackage(Context& context) -> void {
  267. HandlePackageAndLibraryDirectives(context, Lex::TokenKind::Package,
  268. NodeKind::PackageIntroducer,
  269. NodeKind::PackageDirective);
  270. }
  271. auto HandleLibrary(Context& context) -> void {
  272. HandlePackageAndLibraryDirectives(context, Lex::TokenKind::Library,
  273. NodeKind::LibraryIntroducer,
  274. NodeKind::LibraryDirective);
  275. }
  276. } // namespace Carbon::Parse