From b03e02c3a966d3fca497a3341f69dc5ddfcb0d37 Mon Sep 17 00:00:00 2001 From: Andreas Fertig Date: Thu, 22 Oct 2026 19:39:06 +0200 Subject: [PATCH] Support for Clang 23. --- ASTHelpers.cpp | 38 +++++++- CMakeLists.txt | 38 ++++++-- CfrontCodeGenerator.cpp | 7 +- CodeGenerator.cpp | 83 ++++++++++++---- CodeGenerator.h | 26 ++++- CodeGeneratorTypes.h | 3 + CoroutinesCodeGenerator.cpp | 18 +++- Insights.cpp | 7 +- InsightsHelpers.cpp | 186 ++++++++++++++++++++++++++++++++---- InsightsHelpers.h | 11 ++- tests/Issue92.cpp | 1 + tests/Issue92.expect | 3 +- 12 files changed, 359 insertions(+), 62 deletions(-) diff --git a/ASTHelpers.cpp b/ASTHelpers.cpp index e0f8a938..e9dabc90 100644 --- a/ASTHelpers.cpp +++ b/ASTHelpers.cpp @@ -521,7 +521,12 @@ CXXRecordDecl* Struct(std::string_view name) auto& ctx = GetGlobalAST(); return CXXRecordDecl::Create( - ctx, TagTypeKind::Struct, ctx.getTranslationUnitDecl(), {}, {}, &ctx.Idents.get(name), nullptr, false); + ctx, TagTypeKind::Struct, ctx.getTranslationUnitDecl(), {}, {}, &ctx.Idents.get(name), nullptr +#if IS_CLANG_NEWER_THAN(22) +#else + ,false +#endif + ); }; auto* rd = getRecord(); @@ -549,7 +554,15 @@ FieldDecl* mkFieldDecl(DeclContext* dc, std::string_view name, QualType type) InitListExpr* InitList(ArrayRef initExprs, QualType t) { - auto* initList = new(GetGlobalAST()) InitListExpr(GetGlobalAST(), SourceLocation{}, initExprs, SourceLocation{}); + auto* initList = new(GetGlobalAST()) InitListExpr(GetGlobalAST(), + SourceLocation{}, + initExprs, + SourceLocation{} +#if IS_CLANG_NEWER_THAN(22) + , + false +#endif + ); initList->setType(t); return initList; @@ -676,19 +689,34 @@ QualType Typedef(std::string_view name, QualType underlayingType) &ctx.Idents.get(name), ctx.getTrivialTypeSourceInfo(underlayingType)); - return ctx.getTypeDeclType(type); + return +#if IS_CLANG_NEWER_THAN(22) + ctx.getTypedefType(ElaboratedTypeKeyword::None, std::nullopt, type); +#else + ctx.getTypeDeclType(type); +#endif } //----------------------------------------------------------------------------- QualType GetRecordDeclType(const CXXMethodDecl* md) { - return QualType(md->getParent()->getTypeForDecl(), 0); + return +#if IS_CLANG_NEWER_THAN(22) + md->getParent()->getASTContext().getCanonicalTagType(md->getParent()); +#else + QualType(md->getParent()->getTypeForDecl(), 0); +#endif } //----------------------------------------------------------------------------- QualType GetRecordDeclType(const RecordDecl* rd) { - return QualType(rd->getTypeForDecl(), 0); + return +#if IS_CLANG_NEWER_THAN(22) + rd->getASTContext().getCanonicalTagType(rd); +#else + QualType(rd->getTypeForDecl(), 0); +#endif } //----------------------------------------------------------------------------- diff --git a/CMakeLists.txt b/CMakeLists.txt index bccf0571..3e1b312b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,8 +117,8 @@ if (BUILD_INSIGHTS_OUTSIDE_LLVM) endif() endfunction(check_compiler) - check_compiler("GNU" 7.3) - check_compiler("Clang" 6.0) + check_compiler("GNU" 15.1) + check_compiler("Clang" 20.0) check_compiler("MSVC" 19.25.0.0) if(NOT HAVE_COMPILER) @@ -392,6 +392,19 @@ if (BUILD_INSIGHTS_OUTSIDE_LLVM) ) endif() + if(${LLVM_PACKAGE_VERSION_PLAIN} VERSION_GREATER_EQUAL "22.0.0") + set(ADDITIONAL_LIBS + ${ADDITIONAL_LIBS} + clangScalableStaticAnalysisSourceTransformation + clangScalableStaticAnalysisAnalyses + clangScalableStaticAnalysisCore + clangUnifiedSymbolResolution + clangAnalysisLifetimeSafety + clangDependencyScanning + clangOptions + ) + endif() + set(ADDITIONAL_LIBS ${ADDITIONAL_LIBS} clangFrontend @@ -406,6 +419,7 @@ if (BUILD_INSIGHTS_OUTSIDE_LLVM) clangBasic clangRewrite clangSupport + ${CLANG23_LIBS} ${LLVM_LIBS} ${LLVM_SYSTEM_LIBS} ) @@ -548,12 +562,20 @@ if(CLANG_LINK_CLANG_DYLIB) target_link_libraries(insights PRIVATE clang-cpp) else() # general include also provided by clang-build - target_link_libraries(insights - PRIVATE - ${ADDITIONAL_LIBS} - clangTooling - clangASTMatchers - ) + #if(CMAKE_CXX_LINK_GROUP_USING_RESCAN_SUPPORTED) + if(NOT APPLE) + target_link_libraries(insights + PRIVATE + "$" + ) + else() + target_link_libraries(insights + PRIVATE + ${ADDITIONAL_LIBS} + clangTooling + clangASTMatchers + ) + endif() endif() if(LLVM_LINK_LLVM_DYLIB) diff --git a/CfrontCodeGenerator.cpp b/CfrontCodeGenerator.cpp index 1832843a..7bf1cc30 100644 --- a/CfrontCodeGenerator.cpp +++ b/CfrontCodeGenerator.cpp @@ -54,7 +54,12 @@ CfrontCodeGenerator::CfrontVtableData::CfrontVtableData() vtableRecorDecl->completeDefinition(); - vtableRecordType = QualType{vtableRecorDecl->getTypeForDecl(), 0u}; + vtableRecordType = +#if IS_CLANG_NEWER_THAN(22) + vtableRecorDecl->getASTContext().getCanonicalTagType(vtableRecorDecl); +#else + QualType{vtableRecorDecl->getTypeForDecl(), 0u}; +#endif } //----------------------------------------------------------------------------- diff --git a/CodeGenerator.cpp b/CodeGenerator.cpp index 9dca5eb1..22667372 100644 --- a/CodeGenerator.cpp +++ b/CodeGenerator.cpp @@ -355,9 +355,13 @@ void CodeGenerator::InsertArg(const CXXForRangeStmt* rangeForStmt) } //----------------------------------------------------------------------------- -void CodeGenerator::InsertQualifierAndName(const DeclarationName& declName, - const NestedNameSpecifier* qualifier, - const bool hasTemplateKeyword) +void CodeGenerator::InsertQualifierAndName(const DeclarationName& declName, + const NestedNameSpecifier +#if not IS_CLANG_NEWER_THAN(22) + * +#endif + qualifier, + const bool hasTemplateKeyword) { mOutputFormatHelper.Append(ScopeHandler::RemoveCurrentScope(GetNestedName(qualifier)), ValueOrDefault(hasTemplateKeyword, kwTemplateSpace), @@ -365,7 +369,11 @@ void CodeGenerator::InsertQualifierAndName(const DeclarationName& declName, } //----------------------------------------------------------------------------- -void CodeGenerator::InsertNamespace(const NestedNameSpecifier* stmt) +void CodeGenerator::InsertNamespace(const NestedNameSpecifier +#if not IS_CLANG_NEWER_THAN(22) + * +#endif + stmt) { mOutputFormatHelper.Append(ScopeHandler::RemoveCurrentScope(GetNestedName(stmt))); } @@ -2406,8 +2414,14 @@ void CodeGenerator::InsertArg(const DeclRefExpr* stmt) if(const auto* ctx = stmt->getDecl()->getDeclContext(); not ctx->isFunctionOrMethod() and not isa(stmt->getDecl()) and not GetInsightsOptions().UseShow2C) { - if(const auto* qualifier = stmt->getQualifier(); - qualifier and (qualifier->getKind() == NestedNameSpecifier::SpecifierKind::Global)) { + if(const auto qualifier = stmt->getQualifier(); + qualifier and ( +#if IS_CLANG_NEWER_THAN(22) + qualifier.getKind() == NestedNameSpecifier::Kind::Global +#else + qualifier->getKind() == NestedNameSpecifier::SpecifierKind::Global +#endif + )) { // According to // https://clang.llvm.org/doxygen/classclang_1_1NestedNameSpecifier.html#ac707a113605ed4283684b8c05664eb6f // the global specifier is not stored. @@ -3107,20 +3121,33 @@ void CodeGenerator::InsertArg(const TypeAliasDecl* stmt) const bool carriesNamespace{[&] { if(const auto tn = templateSpecializationType->getTemplateName(); (TemplateName::QualifiedTemplate == tn.getKind()) or (TemplateName::DependentTemplate == tn.getKind())) { - const auto* qtn = tn.getAsQualifiedTemplateName(); - - return qtn->getQualifier() != nullptr; + if(const auto* qtn = tn.getAsQualifiedTemplateName()) { + return static_cast(qtn->getQualifier()); + } } return false; }()}; +#if not IS_CLANG_NEWER_THAN(22) if(const auto* elaboratedType = underlyingType->getAs()) { if(templateSpecializationType->isSugared() and not carriesNamespace) { // do this only if the templateSpecializationType does not carry a nestedns InsertNamespace(elaboratedType->getQualifier()); } } +#else + if(TemplateName::DependentTemplate == templateSpecializationType->getTemplateName().getKind()) { + mOutputFormatHelper.Append(GetElaboratedTypeKeyword(templateSpecializationType->getKeyword())); + } + + if(templateSpecializationType->isSugared() and not carriesNamespace) { + // The scope was written in front of the typedef, for example std::chrono::nanoseconds. + if(const auto* typedefType = dyn_cast_or_null(underlyingType.getTypePtrOrNull())) { + InsertNamespace(typedefType->getQualifier()); + } + } +#endif StringStream stream{}; stream.Print(*templateSpecializationType); @@ -3129,21 +3156,16 @@ void CodeGenerator::InsertArg(const TypeAliasDecl* stmt) InsertTemplateArgs(*templateSpecializationType); +#if not IS_CLANG_NEWER_THAN(22) } else if(auto* depSpecType = underlyingType->getAs()) { mOutputFormatHelper.Append(GetElaboratedTypeKeyword(depSpecType->getKeyword())); -#if IS_CLANG_NEWER_THAN(20) InsertNamespace(depSpecType->getDependentTemplateName().getQualifier()); mOutputFormatHelper.Append(kwTemplateSpace, GetName(depSpecType->getDependentTemplateName())); -#else - InsertNamespace(depSpecType->getQualifier()); - - mOutputFormatHelper.Append(kwTemplateSpace, depSpecType->getIdentifier()->getName()); -#endif InsertTemplateArgs(*depSpecType); - +#endif } else { mOutputFormatHelper.Append(GetName(underlyingType)); } @@ -4313,7 +4335,7 @@ void CodeGenerator::InsertArgWithParensIfNeeded(const Stmt* stmt) void CodeGenerator::InsertSuffix(const QualType& type) { if(const auto* typePtr = type.getTypePtrOrNull(); typePtr and typePtr->isBuiltinType()) { - if(const auto* bt = dyn_cast_or_null(typePtr)) { + if(const auto* bt = type->getAs()) { const auto kind = bt->getKind(); mOutputFormatHelper.Append(GetBuiltinTypeSuffix(kind)); @@ -4929,7 +4951,13 @@ void CodeGenerator::InsertFunctionNameWithReturnType(const FunctionDecl& d if(decl.isDeleted()) { mOutputFormatHelper.Append(kwSpaceEqualsDelete); - if(auto* delInfo = decl.getDefalutedOrDeletedInfo()) { + if(auto* delInfo = +#if IS_CLANG_NEWER_THAN(22) + decl.getDefaultedOrDeletedInfo() +#else + decl.getDefalutedOrDeletedInfo() +#endif + ) { WrapInParens([&]() { InsertArg(delInfo->getDeletedMessage()); }, AddSpaceAtTheEnd::No); } else { mOutputFormatHelper.AppendSemiNewLine(); @@ -5070,6 +5098,7 @@ void StructuredBindingsCodeGenerator::InsertArg(const BindingDecl* stmt) // Assume that we are looking at a builtin type. We have to construct the variable declaration information. auto type = stmt->getType(); + bool addConstCast{}; // If we have a holding var we are looking at a user defined type like tuple and those the defaults from above // are wrong. This type contains the variable declaration so we insert this. @@ -5083,8 +5112,18 @@ void StructuredBindingsCodeGenerator::InsertArg(const BindingDecl* stmt) // rvalue reference because then the call is get(std::move(e)) type = holdingVar->getType().getCanonicalType(); + // The holding variable's type is always a reference. Make sure we do not lose it. + if(not type->isReferenceType()) { + type = stmt->getASTContext().getRValueReferenceType(type); + } + bindingStmt = holdingVar->getAnyInitializer(); + // Newer Clang versions no longer add a NoOp cast to bind a `T&&` with const to a prvalue of a non-class type. + addConstCast = type->isRValueReferenceType() and type->getPointeeType().isConstQualified() and + not type->getPointeeType()->isRecordType() and bindingStmt and bindingStmt->isPRValue() and + not isa(bindingStmt->IgnoreParens()); + } else if(not type->isLValueReferenceType()) { type = stmt->getASTContext().getLValueReferenceType(type); } @@ -5095,7 +5134,13 @@ void StructuredBindingsCodeGenerator::InsertArg(const BindingDecl* stmt) GetTypeNameAsParameter(type, GetName(*stmt)), hlpAssing); - InsertArg(bindingStmt); + if(addConstCast) { + mOutputFormatHelper.Append(kwStaticCast, "<"sv, GetName(type->getPointeeType()), ">"sv); + WrapInParens([&] { InsertArg(bindingStmt); }); + + } else { + InsertArg(bindingStmt); + } mOutputFormatHelper.AppendSemiNewLine(); } diff --git a/CodeGenerator.h b/CodeGenerator.h index c9352f1c..63bbeff2 100644 --- a/CodeGenerator.h +++ b/CodeGenerator.h @@ -309,9 +309,13 @@ class CodeGenerator } } - void InsertQualifierAndName(const DeclarationName& declName, - const NestedNameSpecifier* qualifier, - const bool hasTemplateKeyword); + void InsertQualifierAndName(const DeclarationName& declName, + const NestedNameSpecifier +#if not IS_CLANG_NEWER_THAN(22) + * +#endif + qualifier, + const bool hasTemplateKeyword); /// For a special case, when a LambdaExpr occurs in a Constructor from an /// in class initializer, there is a need for a more narrow scope for the \c LAMBDA_SCOPE_HELPER. @@ -326,7 +330,11 @@ class CodeGenerator void InsertTemplateArgsObjectParam(const ArrayRef& array); void InsertTemplateArgsObjectParam(const TemplateParamObjectDecl& param); - void InsertNamespace(const NestedNameSpecifier* namespaceSpecifier); + void InsertNamespace(const NestedNameSpecifier +#if not IS_CLANG_NEWER_THAN(22) + * +#endif + namespaceSpecifier); void ParseDeclContext(const DeclContext* Ctx); STRONG_BOOL(SkipBody); @@ -552,7 +560,15 @@ class CoroutinesCodeGenerator final : public CodeGenerator static inline llvm::DenseMap> mOpaqueValues{}; ///! Keeps track of the current set of opaque value - QualType GetFrameType() const { return QualType(mASTData.mFrameType->getTypeForDecl(), 0); } + QualType GetFrameType() const + { + return +#if IS_CLANG_NEWER_THAN(22) + mASTData.mFrameType->getASTContext().getCanonicalTagType(mASTData.mFrameType); +#else + QualType(mASTData.mFrameType->getTypeForDecl(), 0); +#endif + } QualType GetFramePointerType() const; std::string BuildResumeLabelName(int) const; diff --git a/CodeGeneratorTypes.h b/CodeGeneratorTypes.h index 9aa07225..e5c120be 100644 --- a/CodeGeneratorTypes.h +++ b/CodeGeneratorTypes.h @@ -20,6 +20,9 @@ IGNORED_STMT(OMPParallelForDirective) IGNORED_DECL(UsingShadowDecl) IGNORED_DECL(UsingPackDecl) IGNORED_DECL(EmptyDecl) // stray semicolon? +#if IS_CLANG_NEWER_THAN(22) +IGNORED_DECL(ExplicitInstantiationDecl) +#endif SUPPORTED_DECL(BindingDecl) SUPPORTED_DECL(CXXDeductionGuideDecl) diff --git a/CoroutinesCodeGenerator.cpp b/CoroutinesCodeGenerator.cpp index c9629f77..11232e85 100644 --- a/CoroutinesCodeGenerator.cpp +++ b/CoroutinesCodeGenerator.cpp @@ -549,11 +549,15 @@ void CoroutinesCodeGenerator::InsertCoroutine(const FunctionDecl& fd, const Coro } auto getNonRefType = [&](auto* var) -> QualType { +#if IS_CLANG_NEWER_THAN(22) + return QualType(var->getType().getNonReferenceType().getCanonicalType().getTypePtrOrNull(), 0); +#else if(const auto* et = var->getType().getNonReferenceType()->template getAs()) { return et->getNamedType(); } else { return QualType(var->getType().getNonReferenceType().getTypePtrOrNull(), 0); } +#endif }; SmallVector exprs{}; @@ -570,8 +574,8 @@ void CoroutinesCodeGenerator::InsertCoroutine(const FunctionDecl& fd, const Coro // as it can only be taken as a reference. OnceTrue derefFirstParam{}; - if(not ctor->param_empty() and - (getNonRefType(ctor->getParamDecl(0)) == QualType(cxxMethodType.getTypePtrOrNull(), 0))) { + if(not ctor->param_empty() and (not cxxMethodType.isNull()) and + (getNonRefType(ctor->getParamDecl(0)) == QualType(cxxMethodType.getCanonicalType().getTypePtrOrNull(), 0))) { if(0 == mASTData.mThisExprs.size()) { mASTData.mThisExprs.push_back(CXXThisExpr::Create(ctx, {}, Ptr(cxxMethodType), false)); } @@ -605,7 +609,15 @@ void CoroutinesCodeGenerator::InsertCoroutine(const FunctionDecl& fd, const Coro mOutputFormatHelper.AppendCommentNewLine("Construct the promise."sv); auto* me = AccessMember(mASTData.mFrameAccessDeclRef, mASTData.mPromiseField); - auto* ctorArgs = new(ctx) InitListExpr{ctx, {}, exprs, {}}; + auto* ctorArgs = new(ctx) InitListExpr{ctx, + {}, + exprs, + {} +#if IS_CLANG_NEWER_THAN(22) + , + false +#endif + }; CXXNewExpr* newFrame = New({AddrOf(me)}, ctorArgs, mASTData.mPromiseField->getType()); diff --git a/Insights.cpp b/Insights.cpp index c22ca5c8..b203bfcc 100644 --- a/Insights.cpp +++ b/Insights.cpp @@ -233,6 +233,11 @@ class CppInsightASTConsumer final : public ASTConsumer if(isa(d) and d->isImplicit()) { continue; +#if IS_CLANG_NEWER_THAN(22) + // An explicit instantiation is not transformed, it must not influence the blank line handling. + } else if(isa(d)) { + continue; +#endif // Only handle explicit specializations here. Implicit ones are handled by the `VarTemplateDecl` // itself. } else if(const auto* vdspec = dyn_cast_or_null(d); @@ -393,7 +398,7 @@ extern struct __mptr* __vtbl_array[]; // description). This lets every shell completion script reuse clang's parsing // and show the description alongside the option. The leading "--" is added here // so the bare option name from InsightsOptions.def can be reused directly. -#define INSIGHTS_OPT(option, name, deflt, description, category) \ +#define INSIGHTS_OPT(option, name, deflt, description, category) \ llvm::outs() << "--" << option << "\t" << description << "\n"; #include "InsightsOptions.def" diff --git a/InsightsHelpers.cpp b/InsightsHelpers.cpp index a48d4ab5..fc63f82f 100644 --- a/InsightsHelpers.cpp +++ b/InsightsHelpers.cpp @@ -132,7 +132,12 @@ struct CppInsightsPrintingPolicy : PrintingPolicy SuppressUnwrittenScope = true; Alignof = true; ConstantsAsWritten = true; - AnonymousTagLocations = false; // does remove filename and line for from lambdas in parameters +#if IS_CLANG_NEWER_THAN(22) + AnonymousTagNameStyle = llvm::to_underlying( + AnonymousTagMode::Plain); // does remove filename and line for from lambdas in parameters +#else + AnonymousTagLocations = false; // does remove filename and line for from lambdas in parameters +#endif #if IS_CLANG_NEWER_THAN(20) PrintAsCanonical #else @@ -163,11 +168,17 @@ void ReplaceAll(std::string& str, std::string_view from, std::string_view to) //----------------------------------------------------------------------------- namespace details { -static void -BuildNamespace(std::string& fullNamespace, const NestedNameSpecifier* stmt, const IgnoreNamespace ignoreNamespace) +static void BuildNamespace(std::string& fullNamespace, + const NestedNameSpecifier +#if not IS_CLANG_NEWER_THAN(22) + * +#endif + stmt, + const IgnoreNamespace ignoreNamespace) { RETURN_IF(not stmt); +#if not IS_CLANG_NEWER_THAN(22) if(const auto* prefix = stmt->getPrefix()) { BuildNamespace(fullNamespace, prefix, ignoreNamespace); } @@ -193,6 +204,22 @@ BuildNamespace(std::string& fullNamespace, const NestedNameSpecifier* stmt, cons #endif case NestedNameSpecifier::TypeSpec: fullNamespace.append(GetUnqualifiedScopelessName(stmt->getAsType(), InsightsSuppressScope::Yes)); +#else + switch(stmt.getKind()) { + case NestedNameSpecifier::Kind::Namespace: { + const auto [ns, prefix] = stmt.getAsNamespaceAndPrefix(); + + BuildNamespace(fullNamespace, prefix, ignoreNamespace); + + RETURN_IF((IgnoreNamespace::Yes == ignoreNamespace) or + (isa(ns) and cast(ns)->isAnonymousNamespace())); + + fullNamespace.append(ns->getName()); + } break; + + case NestedNameSpecifier::Kind::Type: + fullNamespace.append(GetUnqualifiedScopelessName(stmt.getAsType(), InsightsSuppressScope::Yes)); +#endif // The template parameters are already contained in the type we inserted above. break; @@ -204,7 +231,12 @@ BuildNamespace(std::string& fullNamespace, const NestedNameSpecifier* stmt, cons //----------------------------------------------------------------------------- } // namespace details -std::string GetNestedName(const NestedNameSpecifier* nns, const IgnoreNamespace ignoreNamespace) +std::string GetNestedName(const NestedNameSpecifier +#if not IS_CLANG_NEWER_THAN(22) + * +#endif + nns, + const IgnoreNamespace ignoreNamespace) { std::string ret{}; @@ -300,11 +332,15 @@ const QualType GetDesugarType(const QualType& QT) if(const auto* autoType = QT->getAs(); autoType and autoType->isSugared()) { const auto dt = autoType->getDeducedType(); +#if IS_CLANG_NEWER_THAN(22) + return dt; +#else if(const auto* et = dt->getAs()) { return et->getNamedType(); } else { return dt; } +#endif } else if(auto declType = QT->getAs()) { return declType->desugar(); @@ -501,7 +537,15 @@ class SimpleTypePrinter return ret; } - bool HandleType(const InjectedClassNameType* type) { return HandleType(type->getInjectedTST()); } + bool HandleType(const InjectedClassNameType* type) + { + return +#if IS_CLANG_NEWER_THAN(22) + HandleType(type->getDecl()->getCanonicalTemplateSpecializationType(GetGlobalAST()).getTypePtr()); +#else + HandleType(type->getInjectedTST()); +#endif + } bool HandleType(const RecordType* type) { @@ -556,6 +600,39 @@ class SimpleTypePrinter } // we need a name here, as DecltypeType always says true +#if IS_CLANG_NEWER_THAN(22) + // A deduced type carries no written scope, so Clang's printer drops it. Add it here. + if(not type->getQualifier()) { + if(const auto scope = GetScope(cxxRecordDecl->getDeclContext(), RemoveCurrentScope::No); + cxxRecordDecl->getDeclContext()->isNamespace() and not scope.empty()) { + mData.Append(ScopeHandler::RemoveCurrentScope(scope + std::string{cxxRecordDecl->getName()})); + + return true; + } + + // A member class of a class template specialization, for example `task::promise_type`. + std::string memberPath{cxxRecordDecl->getName().str()}; + const CXXRecordDecl* ctx = dyn_cast_or_null(cxxRecordDecl->getDeclContext()); + + while(ctx and not isa(ctx)) { + memberPath.insert(0, ctx->getName().str() + "::"); + ctx = dyn_cast_or_null(ctx->getDeclContext()); + } + + if(ctx) { + mData.Append(ScopeHandler::RemoveCurrentScope( + QualType{GetGlobalAST().getCanonicalTagType(ctx)}.getAsString(mPrintingPolicy) + + "::" + memberPath)); + + return true; + } + } + // A scope as written, for example `task::awaiter`. + if(const auto qualifier = type->getQualifier(); qualifier) { + mData.Append(GetNestedName(qualifier)); + } +#endif + mData.Append(GetName(*cxxRecordDecl)); } @@ -569,6 +646,7 @@ class SimpleTypePrinter return HandleType(type->getReplacementType().getTypePtrOrNull()); } +#if not IS_CLANG_NEWER_THAN(22) bool HandleType(const ElaboratedType* type) { const IgnoreNamespace ignoreNamespace = (mPrintingPolicy.CppInsightsSuppressScope == InsightsSuppressScope::Yes) @@ -586,24 +664,17 @@ class SimpleTypePrinter bool HandleType(const DependentTemplateSpecializationType* type) { -#if IS_CLANG_NEWER_THAN(20) mData.Append(GetElaboratedTypeKeyword(type->getKeyword()), GetNestedName(type->getDependentTemplateName().getQualifier()), kwTemplateSpace, GetName(type->getDependentTemplateName())); -#else - mData.Append(GetElaboratedTypeKeyword(type->getKeyword()), - GetNestedName(type->getQualifier()), - kwTemplateSpace, - type->getIdentifier()->getName()); - -#endif CodeGenerator codeGenerator{mData}; codeGenerator.InsertTemplateArgs(*type); return true; } +#endif bool HandleType(const DeducedTemplateSpecializationType* type) { @@ -613,8 +684,22 @@ class SimpleTypePrinter bool HandleType(const TemplateSpecializationType* type) { if(type->getAsRecordDecl()) { +#if IS_CLANG_NEWER_THAN(22) + // A scope written in front of the template name is not part of the canonical record type. + const IgnoreNamespace ignoreNamespace = + (mPrintingPolicy.CppInsightsSuppressScope == InsightsSuppressScope::Yes) ? IgnoreNamespace::No + : IgnoreNamespace::Yes; + BackupAndRestore _{mScope, GetNestedName(type->getTemplateName().getQualifier(), ignoreNamespace)}; +#endif + // Only if it was some sort of "used" `RecordType` we continue here. - if(HandleType(type->getAsRecordDecl()->getTypeForDecl())) { + if(HandleType( +#if IS_CLANG_NEWER_THAN(22) + GetGlobalAST().getCanonicalTagType(type->getAsRecordDecl()).getTypePtr() +#else + type->getAsRecordDecl()->getTypeForDecl() +#endif + )) { HandleType(type->getPointeeType().getTypePtrOrNull()); return true; } @@ -724,8 +809,42 @@ class SimpleTypePrinter return false; } +#if IS_CLANG_NEWER_THAN(22) + bool HandleType(const PredefinedSugarType* type) + { + // Clang prints the sugar as `__size_t`, but we need the builtin type. + mData.Append(type->desugar().getCanonicalType().getAsString(mPrintingPolicy)); + + return true; + } +#endif + bool HandleType(const TypedefType* type) { +#if IS_CLANG_NEWER_THAN(22) + // Clang adds a type suffix like `UL` to integral template arguments whose parameter type is a sugar like + // `size_t`. Print such a member typedef of a template specialization ourselves. + if(const auto* tt = dyn_cast_or_null(type->getDecl()->getDeclContext()); + tt and not mPrintingPolicy.PrintAsCanonical and + (NestedNameSpecifier::Kind::Type == type->getQualifier().getKind()) and + llvm::any_of(tt->getTemplateArgs().asArray(), + [](const auto& arg) { return TemplateArgument::Integral == arg.getKind(); })) { + OutputFormatHelper ofm{}; + CodeGenerator codeGenerator{ofm}; + codeGenerator.InsertTemplateArgs(tt->getTemplateArgs().asArray()); + + // Qualifiers derived from the canonical type are not part of the typedef. + mData.GetString().clear(); + mData.Append(GetScope(tt->getDeclContext(), RemoveCurrentScope::No), + tt->getName(), + ofm, + "::"sv, + type->getDecl()->getName()); + + return true; + } +#endif + if(const auto* decl = type->getDecl()) { return HandleType(decl->getUnderlyingType().getTypePtrOrNull()); } @@ -733,6 +852,24 @@ class SimpleTypePrinter return HandleType(type->getPointeeType().getTypePtrOrNull()); } +#if IS_CLANG_NEWER_THAN(22) + bool HandleType(const UsingType* type) + { + const auto* target = type->getDecl()->getTargetDecl(); + + // Keep the scope as written, for example the `std::` of `std::jmp_buf`, which is a using-declaration. + if(const auto nestedName = GetNestedName(type->getQualifier()); not nestedName.empty()) { + mData.Append(nestedName, target->getName()); + + return true; + } + + mData.Append(GetScope(target->getDeclContext()), target->getName()); + + return true; + } +#endif + bool HandleType(const ConstantArrayType* type) { // Only the outer most ConstantArrayType generates the aary dimensions, block others. @@ -810,15 +947,25 @@ class SimpleTypePrinter HANDLE_TYPE(RecordType); HANDLE_TYPE(AutoType); HANDLE_TYPE(SubstTemplateTypeParmType); +#if not IS_CLANG_NEWER_THAN(22) HANDLE_TYPE(ElaboratedType); +#endif HANDLE_TYPE(TemplateSpecializationType); HANDLE_TYPE(DeducedTemplateSpecializationType); HANDLE_TYPE(MemberPointerType); HANDLE_TYPE(BuiltinType); +#if IS_CLANG_NEWER_THAN(22) + HANDLE_TYPE(PredefinedSugarType); +#endif HANDLE_TYPE(TypedefType); +#if IS_CLANG_NEWER_THAN(22) + HANDLE_TYPE(UsingType); +#endif HANDLE_TYPE(ConstantArrayType); HANDLE_TYPE(InjectedClassNameType); +#if not IS_CLANG_NEWER_THAN(22) HANDLE_TYPE(DependentTemplateSpecializationType); +#endif HANDLE_TYPE(PackExpansionType); HANDLE_TYPE(DecltypeType); @@ -906,12 +1053,14 @@ static std::string GetName(QualType t, // To get the namespace handling right we need to look into the ElaboratedType in some cases. // However, only do that, if the type it not a dependent type. A dependent type might cause type-parameter stuff. +#if not IS_CLANG_NEWER_THAN(22) if(const auto* et = t->getAs(); et and (nullptr == et->getQualifier()) and (nullptr == et->getOwnedTagDecl()) and not et->isDependentType()) { const auto quals = t.getLocalFastQualifiers(); t = et->getNamedType(); t.setLocalFastQualifiers(quals); } +#endif if(true == printingPolicy.CppInsightsUnqualified) { t = t.getUnqualifiedType(); @@ -1306,8 +1455,11 @@ std::string GetTypeNameAsParameter(const QualType& t, std::string_view varName, const auto pointerToArrayBaseType = isAutoType ? t->getContainedAutoType()->getDeducedType() : t; const bool isPointerToArray = HasTypeWithSubType(pointerToArrayBaseType); // Only treat this as an array if it is a top-level arry. Typdef's et all can hide the arrayness. - const bool isRawArrayType = - t->isArrayType() and not(isa(t) or isa(t) or isa(t)); + const bool isRawArrayType = t->isArrayType() and not(isa(t) +#if not IS_CLANG_NEWER_THAN(22) + or isa(t) +#endif + or isa(t)); std::string typeName = details::GetName(t, unqualified); @@ -1422,7 +1574,7 @@ static bool IsTrivialStaticClassVarDecl(const DeclRefExpr& declRefExpr) } //----------------------------------------------------------------------------- -APValue* GetEvaluatedValue(const VarDecl& varDecl) +const APValue* GetEvaluatedValue(const VarDecl& varDecl) { if((nullptr != varDecl.ensureEvaluatedStmt()) and varDecl.ensureEvaluatedStmt()->Value.isValid() and not varDecl.getInit()->isValueDependent()) { diff --git a/InsightsHelpers.h b/InsightsHelpers.h index be6dfbd3..e41508a4 100644 --- a/InsightsHelpers.h +++ b/InsightsHelpers.h @@ -20,6 +20,8 @@ #include "InsightsStrongTypes.h" #include "StackList.h" + +#include "ClangCompat.h" //----------------------------------------------------------------------------- namespace clang::insights { @@ -88,7 +90,7 @@ inline const LangOptions& GetLangOpts(const Decl& decl) /// \brief Get the evaluated APValue from a `VarDecl` /// /// Returns `nullptr` is the \c VarDecl is not evaluatable. -APValue* GetEvaluatedValue(const VarDecl& varDecl); +const APValue* GetEvaluatedValue(const VarDecl& varDecl); //----------------------------------------------------------------------------- /// \brief Check whether a `VarDecl`s initialization can be done a compile-time. @@ -184,7 +186,12 @@ GetTypeNameAsParameter(const QualType& t, std::string_view varName, const Unqual STRONG_BOOL(WithTemplateParameters); STRONG_BOOL(IgnoreNamespace); -std::string GetNestedName(const NestedNameSpecifier* nns, const IgnoreNamespace ignoreNamespace = IgnoreNamespace::No); +std::string GetNestedName(const NestedNameSpecifier +#if not IS_CLANG_NEWER_THAN(22) + * +#endif + nns, + const IgnoreNamespace ignoreNamespace = IgnoreNamespace::No); std::string GetDeclContext(const DeclContext* ctx, WithTemplateParameters withTemplateParameters = WithTemplateParameters::No); //----------------------------------------------------------------------------- diff --git a/tests/Issue92.cpp b/tests/Issue92.cpp index 1511466f..550d7e05 100644 --- a/tests/Issue92.cpp +++ b/tests/Issue92.cpp @@ -5,6 +5,7 @@ #include // std::terminate #include #include +#include template struct generator diff --git a/tests/Issue92.expect b/tests/Issue92.expect index 7beb7dfe..7a139219 100644 --- a/tests/Issue92.expect +++ b/tests/Issue92.expect @@ -3,6 +3,7 @@ #include #include #include +#include template struct generator @@ -121,7 +122,7 @@ struct generator std::coroutine_handle p; }; -/* First instantiated from: Issue92.cpp:76 */ +/* First instantiated from: Issue92.cpp:77 */ #ifdef INSIGHTS_USE_TEMPLATE template<> struct generator