================ @@ -1329,6 +1341,129 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) { return true; } +/// Lex a token following the 'module' contextual keyword. +/// +/// [cpp.module]/p2: +/// The pp-tokens, if any, of a pp-module shall be of the form: +/// pp-module-name pp-module-partition[opt] pp-tokens[opt] +/// +/// where the pp-tokens (if any) shall not begin with a ( preprocessing token +/// and the grammar non-terminals are defined as: +/// pp-module-name: +/// pp-module-name-qualifierp[opt] identifier +/// pp-module-partition: +/// : pp-module-name-qualifier[opt] identifier +/// pp-module-name-qualifier: +/// identifier . +/// pp-module-name-qualifier identifier . +/// No identifier in the pp-module-name or pp-module-partition shall currently +/// be defined as an object-like macro. +/// +/// [cpp.module]/p3: +/// Any preprocessing tokens after the module preprocessing token in the module +/// directive are processed just as in normal text. +bool Preprocessor::LexAfterModuleDecl(Token &Result) { + // Figure out what kind of lexer we actually have. + recomputeCurLexerKind(); + LexUnexpandedToken(Result); + + auto EnterTokens = [this](ArrayRef<Token> Toks, bool DisableMacroExpansion) { + auto ToksCopy = std::make_unique<Token[]>(Toks.size()); + std::copy(Toks.begin(), Toks.end(), ToksCopy.get()); + EnterTokenStream(std::move(ToksCopy), Toks.size(), DisableMacroExpansion, + /*IsReinject=*/false); + }; + + // If we not expect an identifier but got an identifier, it's not a part of + // module name. + if (!ModuleDeclExpectsIdentifier && Result.is(tok::identifier)) { + EnterTokens(Result, /*DisableMacroExpansion=*/false); + return false; + } + + // The token sequence + // + // export[opt] module identifier (. identifier)* + // + // indicates a module import directive. We already saw the 'module' + // contextual keyword, so now we're looking for the identifiers. + if (ModuleDeclExpectsIdentifier && Result.is(tok::identifier)) { + auto *MI = getMacroInfo(Result.getIdentifierInfo()); + if (MI && MI->isObjectLike()) { + auto BuildFixItHint = + [&](std::string &Replacement) -> std::optional<FixItHint> { + EnterTokens(Result, /*DisableMacroExpansion=*/false); + if (!CurTokenLexer) + return std::nullopt; + Token Tok; + bool HasUnknownToken = false; + llvm::raw_string_ostream OS(Replacement); + do { + Lex(Tok); + if (const char *Punc = tok::getPunctuatorSpelling(Tok.getKind())) + OS << Punc; + else if (Tok.isLiteral() && Tok.getLiteralData()) + OS << StringRef(Tok.getLiteralData(), Tok.getLength()); + else if (auto *II = Tok.getIdentifierInfo()) + OS << II->getName(); + else + HasUnknownToken = true; + } while (CurTokenLexer && !CurTokenLexer->isAtEnd()); + if (HasUnknownToken) + return std::nullopt; ---------------- Bigcheese wrote:
I believe this still allows a lot of invalid cases where applying the fixit produces an invalid module name. I expect two different cases of people hitting this. 1. Accidentally use the name of a macro Here what the macro expands to doesn't matter, as they weren't trying to expand it. 2. Intentionally trying to use the preprocessor to set the module name Here the expansion is probably valid, but they went out of their way to use the preprocessor, so they probably don't want to put in what the macro expands to. In either case I'm not sure if the fixit is actually helpful here. My expectation is that Clang will either tell you to replace it with something like `export module static inline __attribute__((always_inline))`, or something you don't want to replace it with anyway. It would be nice in cases like this if we just linked to some documentation saying what to do instead. https://github.com/llvm/llvm-project/pull/90574 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits