Skip to content

[Clang][AST][NFC] (RecordDecl -> CXXRecordDecl)::isInjectedClassName #148195

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
merged 1 commit into from
Jul 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/CodeComplete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,7 @@ bool contextAllowsIndex(enum CodeCompletionContext::Kind K) {
}

static bool isInjectedClass(const NamedDecl &D) {
if (auto *R = dyn_cast_or_null<RecordDecl>(&D))
if (auto *R = dyn_cast_or_null<CXXRecordDecl>(&D))
if (R->isInjectedClassName())
return true;
return false;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/Quality.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ static SymbolRelevanceSignals::AccessibleScope
computeScope(const NamedDecl *D) {
// Injected "Foo" within the class "Foo" has file scope, not class scope.
const DeclContext *DC = D->getDeclContext();
if (auto *R = dyn_cast_or_null<RecordDecl>(D))
if (auto *R = dyn_cast_or_null<CXXRecordDecl>(D))
if (R->isInjectedClassName())
DC = DC->getParent();
// Class constructor should have the same scope as the class.
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/SemanticHighlighting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ class HighlightingsBuilder {
std::optional<HighlightingModifier> scopeModifier(const NamedDecl *D) {
const DeclContext *DC = D->getDeclContext();
// Injected "Foo" within the class "Foo" has file scope, not class scope.
if (auto *R = dyn_cast_or_null<RecordDecl>(D))
if (auto *R = dyn_cast_or_null<CXXRecordDecl>(D))
if (R->isInjectedClassName())
DC = DC->getParent();
// Lambda captures are considered function scope, not class scope.
Expand Down
15 changes: 0 additions & 15 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4420,21 +4420,6 @@ class RecordDecl : public TagDecl {

void reorderDecls(const SmallVectorImpl<Decl *> &Decls);

/// Determines whether this declaration represents the
/// injected class name.
///
/// The injected class name in C++ is the name of the class that
/// appears inside the class itself. For example:
///
/// \code
/// struct C {
/// // C is implicitly declared here as a synonym for the class name.
/// };
///
/// C::C c; // same as "C c;"
/// \endcode
bool isInjectedClassName() const;

/// Determine whether this record is a class describing a lambda
/// function object.
bool isLambda() const;
Expand Down
18 changes: 16 additions & 2 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,7 @@ class CXXRecordDecl : public RecordDecl {
}

CXXRecordDecl *getMostRecentNonInjectedDecl() {
CXXRecordDecl *Recent =
static_cast<CXXRecordDecl *>(this)->getMostRecentDecl();
CXXRecordDecl *Recent = getMostRecentDecl();
while (Recent->isInjectedClassName()) {
// FIXME: Does injected class name need to be in the redeclarations chain?
assert(Recent->getPreviousDecl());
Expand Down Expand Up @@ -1889,6 +1888,21 @@ class CXXRecordDecl : public RecordDecl {
DL.IsGenericLambda = IsGeneric;
}

/// Determines whether this declaration represents the
/// injected class name.
///
/// The injected class name in C++ is the name of the class that
/// appears inside the class itself. For example:
///
/// \code
/// struct C {
/// // C is implicitly declared here as a synonym for the class name.
/// };
///
/// C::C c; // same as "C c;"
/// \endcode
bool isInjectedClassName() const;

// Determine whether this type is an Interface Like type for
// __interface inheritance purposes.
bool isInterfaceLike() const;
Expand Down
5 changes: 0 additions & 5 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5136,11 +5136,6 @@ RecordDecl *RecordDecl::CreateDeserialized(const ASTContext &C,
return R;
}

bool RecordDecl::isInjectedClassName() const {
return isImplicit() && getDeclName() && getDeclContext()->isRecord() &&
cast<RecordDecl>(getDeclContext())->getDeclName() == getDeclName();
}

bool RecordDecl::isLambda() const {
if (auto RD = dyn_cast<CXXRecordDecl>(this))
return RD->isLambda();
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/AST/DeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2149,6 +2149,16 @@ bool CXXRecordDecl::hasDeletedDestructor() const {
return false;
}

bool CXXRecordDecl::isInjectedClassName() const {
if (!isImplicit() || !getDeclName())
return false;

if (const auto *RD = dyn_cast<CXXRecordDecl>(getDeclContext()))
return RD->getDeclName() == getDeclName();

return false;
}

static bool isDeclContextInNamespace(const DeclContext *DC) {
while (!DC->isTranslationUnit()) {
if (DC->isNamespace())
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaAccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1129,7 +1129,8 @@ static void diagnoseBadDirectAccess(Sema &S,
else if (TypedefNameDecl *TND = dyn_cast<TypedefNameDecl>(D))
PrevDecl = TND->getPreviousDecl();
else if (TagDecl *TD = dyn_cast<TagDecl>(D)) {
if (isa<RecordDecl>(D) && cast<RecordDecl>(D)->isInjectedClassName())
if (auto *RD = dyn_cast<CXXRecordDecl>(D);
RD && RD->isInjectedClassName())
break;
PrevDecl = TD->getPreviousDecl();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener {
// Filter out decls that we can't complete later.
if (!isa<TagDecl>(to) && !isa<ObjCInterfaceDecl>(to))
return;
RecordDecl *from_record_decl = dyn_cast<RecordDecl>(from);
auto *from_record_decl = dyn_cast<CXXRecordDecl>(from);
// We don't need to complete injected class name decls.
if (from_record_decl && from_record_decl->isInjectedClassName())
return;
Expand Down
5 changes: 4 additions & 1 deletion lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2420,9 +2420,12 @@ void TypeSystemClang::DumpDeclHiearchy(clang::Decl *decl) {

clang::RecordDecl *record_decl = llvm::dyn_cast<clang::RecordDecl>(decl);
if (record_decl) {
bool is_injected_class_name =
llvm::isa<clang::CXXRecordDecl>(record_decl) &&
llvm::cast<CXXRecordDecl>(record_decl)->isInjectedClassName();
printf("%20s: %s%s\n", decl->getDeclKindName(),
record_decl->getDeclName().getAsString().c_str(),
record_decl->isInjectedClassName() ? " (injected class name)" : "");
is_injected_class_name ? " (injected class name)" : "");

} else {
clang::NamedDecl *named_decl = llvm::dyn_cast<clang::NamedDecl>(decl);
Expand Down
Loading