-
Notifications
You must be signed in to change notification settings - Fork 14.3k
Revert "[Clang] Implement P3034R1 Module Declarations Shouldn’t be Macros" #99838
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@llvm/pr-subscribers-clang-modules @llvm/pr-subscribers-clang Author: None (yronglin) ChangesReverts llvm/llvm-project#90574 Patch is 55.48 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/99838.diff 19 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4ab6bd9de8ea9..7ac6ed934290d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -285,8 +285,6 @@ C++2c Feature Support
- Implemented `P2963R3 Ordering of constraints involving fold expressions <https://wg21.link/P2963R3>`_.
-- Implemented `P3034R1 Module Declarations Shouldn’t be Macros <https://wg21.link/P3034R1>`_.
-
Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 08ece01009387..12d7b8c0205ee 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -952,11 +952,6 @@ def warn_module_conflict : Warning<
InGroup<ModuleConflict>;
// C++20 modules
-def err_module_decl_cannot_be_macros : Error<
- "the module name in a module%select{| partition}0 declaration cannot contain "
- "an object-like macro %1">;
-def err_unxepected_paren_in_module_decl : Error<
- "unexpected '(' after the module name in a module%select{| partition}0 declaration">;
def err_header_import_semi_in_macro : Error<
"semicolon terminating header import declaration cannot be produced "
"by a macro">;
diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h
index f40f74d0355ad..ae9ebd9f59154 100644
--- a/clang/include/clang/Basic/IdentifierTable.h
+++ b/clang/include/clang/Basic/IdentifierTable.h
@@ -180,10 +180,6 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
LLVM_PREFERRED_TYPE(bool)
unsigned IsModulesImport : 1;
- // True if this is the 'module' contextual keyword.
- LLVM_PREFERRED_TYPE(bool)
- unsigned IsModulesDecl : 1;
-
// True if this is a mangled OpenMP variant name.
LLVM_PREFERRED_TYPE(bool)
unsigned IsMangledOpenMPVariantName : 1;
@@ -200,7 +196,7 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
LLVM_PREFERRED_TYPE(bool)
unsigned IsFinal : 1;
- // 21 bits left in a 64-bit word.
+ // 22 bits left in a 64-bit word.
// Managed by the language front-end.
void *FETokenInfo = nullptr;
@@ -216,8 +212,8 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
IsCPPOperatorKeyword(false), NeedsHandleIdentifier(false),
IsFromAST(false), ChangedAfterLoad(false), FEChangedAfterLoad(false),
RevertedTokenID(false), OutOfDate(false), IsModulesImport(false),
- IsModulesDecl(false), IsMangledOpenMPVariantName(false),
- IsDeprecatedMacro(false), IsRestrictExpansion(false), IsFinal(false) {}
+ IsMangledOpenMPVariantName(false), IsDeprecatedMacro(false),
+ IsRestrictExpansion(false), IsFinal(false) {}
public:
IdentifierInfo(const IdentifierInfo &) = delete;
@@ -524,18 +520,6 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
RecomputeNeedsHandleIdentifier();
}
- /// Determine whether this is the contextual keyword \c module.
- bool isModulesDeclaration() const { return IsModulesDecl; }
-
- /// Set whether this identifier is the contextual keyword \c module.
- void setModulesDeclaration(bool I) {
- IsModulesDecl = I;
- if (I)
- NeedsHandleIdentifier = true;
- else
- RecomputeNeedsHandleIdentifier();
- }
-
/// Determine whether this is the mangled name of an OpenMP variant.
bool isMangledOpenMPVariantName() const { return IsMangledOpenMPVariantName; }
@@ -756,8 +740,6 @@ class IdentifierTable {
// If this is the 'import' contextual keyword, mark it as such.
if (Name == "import")
II->setModulesImport(true);
- else if (Name == "module")
- II->setModulesDeclaration(true);
return *II;
}
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 8db18c049b6d0..7f4912b9bcd96 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -1003,9 +1003,6 @@ ANNOTATION(module_include)
ANNOTATION(module_begin)
ANNOTATION(module_end)
-// Annotations for C++, Clang and Objective-C named modules.
-ANNOTATION(module_name)
-
// Annotation for a header_name token that has been looked up and transformed
// into the name of a header unit.
ANNOTATION(header_unit)
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 56aef99a3f38a..fc7d0053f2323 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -615,6 +615,10 @@ class Preprocessor {
ModuleDeclSeq ModuleDeclState;
+ /// Whether the module import expects an identifier next. Otherwise,
+ /// it expects a '.' or ';'.
+ bool ModuleImportExpectsIdentifier = false;
+
/// The identifier and source location of the currently-active
/// \#pragma clang arc_cf_code_audited begin.
std::pair<IdentifierInfo *, SourceLocation> PragmaARCCFCodeAuditedInfo;
@@ -1740,14 +1744,11 @@ class Preprocessor {
/// Lex a token, forming a header-name token if possible.
bool LexHeaderName(Token &Result, bool AllowMacroExpansion = true);
- /// Lex a module name or a partition name.
- bool LexModuleName(Token &Result, bool IsImport);
-
/// Lex the parameters for an #embed directive, returns nullopt on error.
std::optional<LexEmbedParametersResult> LexEmbedParameters(Token &Current,
bool ForHasEmbed);
+
bool LexAfterModuleImport(Token &Result);
- bool LexAfterModuleDecl(Token &Result);
void CollectPpImportSuffix(SmallVectorImpl<Token> &Toks);
void makeModuleVisible(Module *M, SourceLocation Loc);
@@ -3038,9 +3039,6 @@ class Preprocessor {
static bool CLK_LexAfterModuleImport(Preprocessor &P, Token &Result) {
return P.LexAfterModuleImport(Result);
}
- static bool CLK_LexAfterModuleDecl(Preprocessor &P, Token &Result) {
- return P.LexAfterModuleDecl(Result);
- }
};
/// Abstract base class that describes a handler that will receive
@@ -3073,77 +3071,6 @@ struct EmbedAnnotationData {
/// Registry of pragma handlers added by plugins
using PragmaHandlerRegistry = llvm::Registry<PragmaHandler>;
-/// Represents module or partition name token sequance.
-///
-/// module-name:
-/// module-name-qualifier[opt] identifier
-///
-/// partition-name: [C++20]
-/// : module-name-qualifier[opt] identifier
-///
-/// module-name-qualifier
-/// module-name-qualifier[opt] identifier .
-///
-/// This class can only be created by the preprocessor and guarantees that the
-/// two source array being contiguous in memory and only contains 3 kind of
-/// tokens (identifier, '.' and ':'). And only available when the preprocessor
-/// returns annot_module_name token.
-///
-/// For exmaple:
-///
-/// export module m.n:c.d
-///
-/// The module name array has 3 tokens ['m', '.', 'n'].
-/// The partition name array has 4 tokens [':', 'c', '.', 'd'].
-///
-/// When import a partition in a named module fragment (Eg. import :part1;),
-/// the module name array will be empty, and the partition name array has 2
-/// tokens.
-///
-/// When we meet a private-module-fragment (Eg. module :private;), preprocessor
-/// will not return a annot_module_name token, but will return 2 separate tokens
-/// [':', 'kw_private'].
-
-class ModuleNameInfo {
- friend class Preprocessor;
- ArrayRef<Token> ModuleName;
- ArrayRef<Token> PartitionName;
-
- ModuleNameInfo(ArrayRef<Token> AnnotToks, std::optional<unsigned> ColonIndex);
-
-public:
- /// Return the contiguous token array.
- ArrayRef<Token> getTokens() const {
- if (ModuleName.empty())
- return PartitionName;
- if (PartitionName.empty())
- return ModuleName;
- return ArrayRef(ModuleName.begin(), PartitionName.end());
- }
- bool hasModuleName() const { return !ModuleName.empty(); }
- bool hasPartitionName() const { return !PartitionName.empty(); }
- ArrayRef<Token> getModuleName() const { return ModuleName; }
- ArrayRef<Token> getPartitionName() const { return PartitionName; }
- Token getColonToken() const {
- assert(hasPartitionName() && "Do not have a partition name");
- return getPartitionName().front();
- }
-
- /// Under the standard C++ Modules, the dot is just part of the module name,
- /// and not a real hierarchy separator. Flatten such module names now.
- std::string getFlatName() const;
-
- /// Build a module id path from the contiguous token array, both include
- /// module name and partition name.
- void getModuleIdPath(
- SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path) const;
-
- /// Build a module id path from \param ModuleName.
- static void getModuleIdPath(
- ArrayRef<Token> ModuleName,
- SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path);
-};
-
} // namespace clang
#endif // LLVM_CLANG_LEX_PREPROCESSOR_H
diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h
index 2be3ad39529f0..4f29fb7d11415 100644
--- a/clang/include/clang/Lex/Token.h
+++ b/clang/include/clang/Lex/Token.h
@@ -235,9 +235,6 @@ class Token {
assert(isAnnotation() && "Used AnnotVal on non-annotation token");
return PtrData;
}
- template <class T> T getAnnotationValueAs() const {
- return static_cast<T>(getAnnotationValue());
- }
void setAnnotationValue(void *val) {
assert(isAnnotation() && "Used AnnotVal on non-annotation token");
PtrData = val;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index afcdacf02583a..93e60be512aae 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3876,7 +3876,7 @@ class Parser : public CodeCompletionHandler {
}
bool ParseModuleName(
- SourceLocation UseLoc, ArrayRef<Token> ModuleName,
+ SourceLocation UseLoc,
SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
bool IsImport);
diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp
index 97d830214f890..4f7ccaf4021d6 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -322,9 +322,8 @@ void IdentifierTable::AddKeywords(const LangOptions &LangOpts) {
if (LangOpts.IEEE128)
AddKeyword("__ieee128", tok::kw___float128, KEYALL, LangOpts, *this);
- // Add the 'import' and 'module' contextual keyword.
+ // Add the 'import' contextual keyword.
get("import").setModulesImport(true);
- get("module").setModulesDeclaration(true);
}
/// Checks if the specified token kind represents a keyword in the
diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp
index 1fff88ccf0405..0592423c12eca 100644
--- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp
+++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp
@@ -758,10 +758,9 @@ void PrintPPOutputPPCallbacks::HandleWhitespaceBeforeTok(const Token &Tok,
// These tokens are not expanded to anything and don't need whitespace before
// them.
if (Tok.is(tok::eof) ||
- (Tok.isAnnotation() && Tok.isNot(tok::annot_header_unit) &&
- Tok.isNot(tok::annot_module_begin) && Tok.isNot(tok::annot_module_end) &&
- Tok.isNot(tok::annot_module_name) &&
- Tok.isNot(tok::annot_repl_input_end) && Tok.isNot(tok::annot_embed)))
+ (Tok.isAnnotation() && !Tok.is(tok::annot_header_unit) &&
+ !Tok.is(tok::annot_module_begin) && !Tok.is(tok::annot_module_end) &&
+ !Tok.is(tok::annot_repl_input_end) && !Tok.is(tok::annot_embed)))
return;
// EmittedDirectiveOnThisLine takes priority over RequireSameLine.
@@ -952,11 +951,6 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok,
PP.Lex(Tok);
IsStartOfLine = true;
continue;
- } else if (Tok.is(tok::annot_module_name)) {
- auto *Info = static_cast<ModuleNameInfo *>(Tok.getAnnotationValue());
- *Callbacks->OS << Info->getFlatName();
- PP.Lex(Tok);
- continue;
} else if (Tok.is(tok::annot_header_unit)) {
// This is a header-name that has been (effectively) converted into a
// module-name.
diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp
index c3a903917e9ce..8221db46e06ac 100644
--- a/clang/lib/Lex/PPLexerChange.cpp
+++ b/clang/lib/Lex/PPLexerChange.cpp
@@ -122,8 +122,7 @@ void Preprocessor::EnterSourceFileWithLexer(Lexer *TheLexer,
CurPPLexer = TheLexer;
CurDirLookup = CurDir;
CurLexerSubmodule = nullptr;
- if (CurLexerCallback != CLK_LexAfterModuleImport &&
- CurLexerCallback != CLK_LexAfterModuleDecl)
+ if (CurLexerCallback != CLK_LexAfterModuleImport)
CurLexerCallback = TheLexer->isDependencyDirectivesLexer()
? CLK_DependencyDirectivesLexer
: CLK_Lexer;
@@ -162,7 +161,8 @@ void Preprocessor::EnterMacro(Token &Tok, SourceLocation ILEnd,
PushIncludeMacroStack();
CurDirLookup = nullptr;
CurTokenLexer = std::move(TokLexer);
- CurLexerCallback = CLK_TokenLexer;
+ if (CurLexerCallback != CLK_LexAfterModuleImport)
+ CurLexerCallback = CLK_TokenLexer;
}
/// EnterTokenStream - Add a "macro" context to the top of the include stack,
@@ -216,8 +216,7 @@ void Preprocessor::EnterTokenStream(const Token *Toks, unsigned NumToks,
PushIncludeMacroStack();
CurDirLookup = nullptr;
CurTokenLexer = std::move(TokLexer);
- if (CurLexerCallback != CLK_LexAfterModuleImport &&
- CurLexerCallback != CLK_LexAfterModuleDecl)
+ if (CurLexerCallback != CLK_LexAfterModuleImport)
CurLexerCallback = CLK_TokenLexer;
}
diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 2726fae344337..63e27e62cffc8 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -860,15 +860,9 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
ModuleImportLoc = Identifier.getLocation();
NamedModuleImportPath.clear();
IsAtImport = true;
+ ModuleImportExpectsIdentifier = true;
CurLexerCallback = CLK_LexAfterModuleImport;
}
-
- if ((II.isModulesDeclaration() || Identifier.is(tok::kw_module)) &&
- !InMacroArgs && !DisableMacroExpansion &&
- (getLangOpts().CPlusPlusModules || getLangOpts().DebuggerSupport) &&
- CurLexerCallback != CLK_CachingLexer) {
- CurLexerCallback = CLK_LexAfterModuleDecl;
- }
return true;
}
@@ -911,7 +905,6 @@ void Preprocessor::Lex(Token &Result) {
// This token is injected to represent the translation of '#include "a.h"'
// into "import a.h;". Mimic the notional ';'.
case tok::annot_module_include:
- case tok::annot_repl_input_end:
case tok::semi:
TrackGMFState.handleSemi();
StdCXXImportSeqState.handleSemi();
@@ -926,30 +919,12 @@ void Preprocessor::Lex(Token &Result) {
StdCXXImportSeqState.handleExport();
ModuleDeclState.handleExport();
break;
- case tok::annot_module_name: {
- auto *Info = static_cast<ModuleNameInfo *>(Result.getAnnotationValue());
- for (const auto &Tok : Info->getTokens()) {
- switch (Tok.getKind()) {
- case tok::identifier:
- ModuleDeclState.handleIdentifier(Tok.getIdentifierInfo());
- break;
- case tok::period:
- ModuleDeclState.handlePeriod();
- break;
- case tok::colon:
- ModuleDeclState.handleColon();
- break;
- default:
- llvm_unreachable("Unexpected token in module name");
- }
- }
- if (ModuleDeclState.isModuleCandidate())
- break;
- TrackGMFState.handleMisc();
- StdCXXImportSeqState.handleMisc();
- ModuleDeclState.handleMisc();
+ case tok::colon:
+ ModuleDeclState.handleColon();
+ break;
+ case tok::period:
+ ModuleDeclState.handlePeriod();
break;
- }
case tok::identifier:
// Check "import" and "module" when there is no open bracket. The two
// identifiers are not meaningful with open brackets.
@@ -961,17 +936,17 @@ void Preprocessor::Lex(Token &Result) {
ModuleImportLoc = Result.getLocation();
NamedModuleImportPath.clear();
IsAtImport = false;
+ ModuleImportExpectsIdentifier = true;
CurLexerCallback = CLK_LexAfterModuleImport;
}
break;
- }
- if (Result.getIdentifierInfo()->isModulesDeclaration()) {
+ } else if (Result.getIdentifierInfo() == getIdentifierInfo("module")) {
TrackGMFState.handleModule(StdCXXImportSeqState.afterTopLevelSeq());
ModuleDeclState.handleModule();
- CurLexerCallback = CLK_LexAfterModuleDecl;
break;
}
}
+ ModuleDeclState.handleIdentifier(Result.getIdentifierInfo());
if (ModuleDeclState.isModuleCandidate())
break;
[[fallthrough]];
@@ -1146,151 +1121,6 @@ void Preprocessor::CollectPpImportSuffix(SmallVectorImpl<Token> &Toks) {
}
}
-ModuleNameInfo::ModuleNameInfo(ArrayRef<Token> AnnotToks,
- std::optional<unsigned> ColonIndex) {
- assert(!AnnotToks.empty() && "Named module token cannot be empty.");
- if (!ColonIndex.has_value())
- ColonIndex = AnnotToks.size();
- ModuleName = ArrayRef(AnnotToks.begin(), AnnotToks.begin() + *ColonIndex);
- PartitionName = ArrayRef(AnnotToks.begin() + *ColonIndex, AnnotToks.end());
- assert(ModuleName.end() == PartitionName.begin());
-}
-
-std::string ModuleNameInfo::getFlatName() const {
- std::string FlatModuleName;
- for (auto &Tok : getTokens()) {
- switch (Tok.getKind()) {
- case tok::identifier:
- FlatModuleName += Tok.getIdentifierInfo()->getName();
- break;
- case tok::period:
- FlatModuleName += '.';
- break;
- case tok::colon:
- FlatModuleName += ':';
- break;
- default:
- llvm_unreachable("Unexpected token in module name");
- }
- }
- return FlatModuleName;
-}
-
-void ModuleNameInfo::getModuleIdPath(
- SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path) const {
- return getModuleIdPath(getTokens(), Path);
-}
-
-void ModuleNameInfo::getModuleIdPath(
- ArrayRef<Token> ModuleName,
- SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path) {
- for (const auto &Tok : ModuleName) {
- if (Tok.is(tok::identifier))
- Path.push_back(
- std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
- }
-}
-
-/// Lex a module name or a partition name.
-///
-/// module-name:
-/// module-name-qualifier[opt] identifier
-///
-/// partition-name: [C++20]
-/// : module-name-qualifier[opt] identifier
-///
-/// module-name-qualifier
-/// module-name-qualifier[opt] identifier .
-bool Preprocessor::LexModuleName(Token &Result, bool IsImport) {
- bool ExpectsIdentifier = true, IsLexingPartition = false;
- SmallVector<Token, 8> ModuleName;
- std::optional<unsigned> ColonTokIndex;
- auto LexNextToken = [&](Token &Tok) {
- if (IsImport)
- Lex(Tok);
- else
- LexUnexpandedToken(Tok);
- };
-
- while (true) {
- LexNextToken(Result);
- if (ExpectsIdentifier && Result.is(tok::identifier)) {
- auto *MI = getMacroInfo(Result.getIdentifierInfo());
- if (getLangOpts().CPlusPlusModules && !IsImport && MI &&
- MI->isObjectLike()) {
- Diag(Result, diag::err_module_decl_cannot_be_macros)
- << Result.getLocation() << IsLexingPartition
- << Result.getIdentifierInfo();
- }
- ModuleName.push_back(Result);
- ExpectsIdentifier = false;
- continue;
- }
-
- if (!ExpectsIdentifier && Result.is(tok::period)) {
- ModuleName.push_back(Result);
- ExpectsIdentifier = true;
- continue;
- }
-
- // Module partition only allowed in C++20 Modules.
- if (getLangOpts().CPlusPlusModules && Result.is(tok::colon)) {
- // Handle the form like: import :P;
- // If the token after ':' is not an identifier, this is a invalid module
- // name.
- if (ModuleName.empty()) {
- Token Tmp;
- LexNextToken(Tmp);
- EnterToken(Tmp, /*IsReiject=*/false);
- // A private-module-fragment:
- // export module :private;
- if (!IsImport && Tmp.is(tok::kw_private))
- return true;
- // import :N;
-...
[truncated]
|
yuxuanchen1997
pushed a commit
that referenced
this pull request
Jul 25, 2024
…cros" (#99838) Summary: Reverts #90574 Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D60251154
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
clang:frontend
Language frontend issues, e.g. anything involving "Sema"
clang:modules
C++20 modules and Clang Header Modules
clang
Clang issues not falling into any other category
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Reverts #90574