Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TClingUtils.cxx
Go to the documentation of this file.
1// @(#)root/metautils:$Id$
2// Author: Paul Russo, 2009-10-06
3
4/*************************************************************************
5 * Copyright (C) 1995-2011, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12//______________________________________________________________________________
13// //
14// ROOT::TMetaUtils provides utility wrappers around //
15// cling, the LLVM-based interpreter. It's an internal set of tools //
16// used by TCling and rootcling. //
17// //
18//______________________________________________________________________________
19#include <algorithm>
20#include <iostream>
21#include <sstream>
22#include <cstdlib>
23#include <cstdio>
24#include <unordered_set>
25#include <cctype>
26
27#include "RConfigure.h"
29#include <ROOT/RConfig.hxx>
31#include "Rtypes.h"
32#include "strlcpy.h"
33
34#include "RStl.h"
35
36#include "clang/AST/ASTContext.h"
37#include "clang/AST/Attr.h"
38#include "clang/AST/CXXInheritance.h"
39#include "clang/AST/Decl.h"
40#include "clang/AST/DeclTemplate.h"
41#include "clang/AST/Mangle.h"
42#include "clang/AST/Type.h"
43#include "clang/AST/TypeVisitor.h"
44#include "clang/Frontend/CompilerInstance.h"
45#include "clang/Lex/HeaderSearch.h"
46#include "clang/Lex/ModuleMap.h"
47#include "clang/Lex/Preprocessor.h"
48#include "clang/AST/QualTypeNames.h"
49#include "clang/Lex/PreprocessorOptions.h"
50
51#include "clang/Sema/Lookup.h"
52#include "clang/Sema/Sema.h"
53#include "clang/Sema/SemaDiagnostic.h"
54
55#include "cling/Interpreter/LookupHelper.h"
56#include "cling/Interpreter/Transaction.h"
57#include "cling/Interpreter/Interpreter.h"
58#include "cling/Utils/AST.h"
59#include "cling/Interpreter/InterpreterAccessRAII.h"
60
61#include "llvm/Support/Path.h"
62#include "llvm/Support/FileSystem.h"
63
64#include "TClingUtils.h"
65
66#ifdef _WIN32
67#define strncasecmp _strnicmp
68#include <io.h>
69#else
70#include <unistd.h>
71#endif // _WIN32
72
73namespace ROOT {
74namespace TMetaUtils {
75
76std::string GetRealPath(const std::string &path)
77{
78 llvm::SmallString<256> result_path;
79 llvm::sys::fs::real_path(path, result_path, /*expandTilde*/true);
80 return result_path.str().str();
81}
82
83
84////////////////////////////////////////////////////////////////////////////////
85
87 using DeclsCont_t = TNormalizedCtxt::Config_t::SkipCollection;
91private:
95public:
96 TNormalizedCtxtImpl(const cling::LookupHelper &lh);
97
98 const Config_t &GetConfig() const { return fConfig; }
100 void AddTemplAndNargsToKeep(const clang::ClassTemplateDecl* templ, unsigned int i);
101 int GetNargsToKeep(const clang::ClassTemplateDecl* templ) const;
103 void keepTypedef(const cling::LookupHelper &lh, const char* name,
104 bool replace = false);
105};
106}
107}
108
109namespace {
110
111////////////////////////////////////////////////////////////////////////////////
112/// Add default parameter to the scope if needed.
113
114static clang::NestedNameSpecifier AddDefaultParametersNNS(const clang::ASTContext& Ctx,
115 clang::NestedNameSpecifier scope,
116 const cling::Interpreter &interpreter,
118 if (scope.getKind() == clang::NestedNameSpecifier::Kind::Type) {
119 const clang::Type* scope_type = scope.getAsType();
120 // this is not a namespace, so we might need to desugar
121 clang::NestedNameSpecifier outer_scope = scope.getAsType()->getPrefix();
122 if (outer_scope) {
124 }
125
126 clang::QualType addDefault =
128 // NOTE: Should check whether the type has changed or not.
129 if (addDefault.getTypePtr() != scope_type)
130 return clang::NestedNameSpecifier(addDefault.getTypePtr());
131 }
132 return scope;
133}
134
135////////////////////////////////////////////////////////////////////////////////
136
137static bool CheckDefinition(const clang::CXXRecordDecl *cl, const clang::CXXRecordDecl *context)
138{
139 if (!cl->hasDefinition()) {
140 if (context) {
141 ROOT::TMetaUtils::Error("CheckDefinition",
142 "Missing definition for class %s, please #include its header in the header of %s\n",
143 cl->getName().str().c_str(), context->getName().str().c_str());
144 } else {
145 ROOT::TMetaUtils::Error("CheckDefinition",
146 "Missing definition for class %s\n",
147 cl->getName().str().c_str());
148 }
149 return false;
150 }
151 return true;
152}
153
154////////////////////////////////////////////////////////////////////////////////
155
156static bool IsTypeInt(const clang::Type *type)
157{
158 const clang::BuiltinType * builtin = llvm::dyn_cast<clang::BuiltinType>(type->getCanonicalTypeInternal().getTypePtr());
159 if (builtin) {
160 return builtin->isInteger(); // builtin->getKind() == clang::BuiltinType::Int;
161 } else {
162 return false;
163 }
164}
165
166////////////////////////////////////////////////////////////////////////////////
167
168static bool IsFieldDeclInt(const clang::FieldDecl *field)
169{
170 return IsTypeInt(field->getType().getTypePtr());
171}
172
173////////////////////////////////////////////////////////////////////////////////
174/// Return a data member name 'what' in the class described by 'cl' if any.
175
176static const clang::FieldDecl *GetDataMemberFromAll(const clang::CXXRecordDecl &cl, llvm::StringRef what)
177{
178 clang::ASTContext &C = cl.getASTContext();
179 clang::DeclarationName DName = &C.Idents.get(what);
180 auto R = cl.lookup(DName);
181 for (const clang::NamedDecl *D : R)
183 return FD;
184 return nullptr;
185}
186
187////////////////////////////////////////////////////////////////////////////////
188/// Return a data member name 'what' in any of the base classes of the class described by 'cl' if any.
189
190static const clang::FieldDecl *GetDataMemberFromAllParents(clang::Sema &SemaR, const clang::CXXRecordDecl &cl, const char *what)
191{
192 clang::DeclarationName DName = &SemaR.Context.Idents.get(what);
193 clang::LookupResult R(SemaR, DName, clang::SourceLocation(),
194 clang::Sema::LookupOrdinaryName,
195 RedeclarationKind::ForExternalRedeclaration);
196 SemaR.LookupInSuper(R, &const_cast<clang::CXXRecordDecl&>(cl));
197 if (R.empty())
198 return nullptr;
199 return llvm::dyn_cast<const clang::FieldDecl>(R.getFoundDecl());
200}
201
202static
203cling::LookupHelper::DiagSetting ToLHDS(bool wantDiags) {
204 return wantDiags
205 ? cling::LookupHelper::WithDiagnostics
206 : cling::LookupHelper::NoDiagnostics;
207}
208
209} // end of anonymous namespace
210
211
212namespace ROOT {
213namespace TMetaUtils {
214
215////////////////////////////////////////////////////////////////////////////////
216/// Add to the internal map the pointer of a template as key and the number of
217/// template arguments to keep as value.
218
219void TNormalizedCtxtImpl::AddTemplAndNargsToKeep(const clang::ClassTemplateDecl* templ,
220 unsigned int i){
221 if (!templ){
222 Error("TNormalizedCtxt::AddTemplAndNargsToKeep",
223 "Tring to specify a number of template arguments to keep for a null pointer. Exiting without assigning any value.\n");
224 return;
225 }
226
227 const clang::ClassTemplateDecl* canTempl = templ->getCanonicalDecl();
228
229 if(fTemplatePtrArgsToKeepMap.count(canTempl)==1 &&
231 const std::string templateName (canTempl->getNameAsString());
232 const std::string i_str (std::to_string(i));
233 const std::string previousArgsToKeep(std::to_string(fTemplatePtrArgsToKeepMap[canTempl]));
234 Error("TNormalizedCtxt::AddTemplAndNargsToKeep",
235 "Tring to specify for template %s %s arguments to keep, while before this number was %s\n",
236 canTempl->getNameAsString().c_str(),
237 i_str.c_str(),
238 previousArgsToKeep.c_str());
239 }
240
242}
243////////////////////////////////////////////////////////////////////////////////
244/// Get from the map the number of arguments to keep.
245/// It uses the canonical decl of the template as key.
246/// If not present, returns -1.
247
248int TNormalizedCtxtImpl::GetNargsToKeep(const clang::ClassTemplateDecl* templ) const{
249 const clang::ClassTemplateDecl* constTempl = templ->getCanonicalDecl();
251 int nArgsToKeep = (thePairPtr != fTemplatePtrArgsToKeepMap.end() ) ? thePairPtr->second : -1;
252 return nArgsToKeep;
253}
254
255
256////////////////////////////////////////////////////////////////////////////////
257
258TNormalizedCtxt::TNormalizedCtxt(const cling::LookupHelper &lh):
260{}
261
265
275void TNormalizedCtxt::AddTemplAndNargsToKeep(const clang::ClassTemplateDecl* templ, unsigned int i)
276{
278}
279int TNormalizedCtxt::GetNargsToKeep(const clang::ClassTemplateDecl* templ) const
280{
281 return fImpl->GetNargsToKeep(templ);
282}
286void TNormalizedCtxt::keepTypedef(const cling::LookupHelper &lh, const char* name,
287 bool replace /*= false*/)
288{
289 return fImpl->keepTypedef(lh, name, replace);
290}
291
292std::string AnnotatedRecordDecl::BuildDemangledTypeInfo(const clang::RecordDecl *rDecl,
293 const std::string &normalizedName)
294{
295 // Types with strong typedefs must not be findable through demangled type names, or else
296 // the demangled name will resolve to both sinblings double / Double32_t.
297 if (normalizedName.find("Double32_t") != std::string::npos
298 || normalizedName.find("Float16_t") != std::string::npos)
299 return {};
300 std::unique_ptr<clang::MangleContext> mangleCtx(rDecl->getASTContext().createMangleContext());
301 std::string mangledName;
302 {
303 llvm::raw_string_ostream sstr(mangledName);
304 if (const clang::TypeDecl* TD = llvm::dyn_cast<clang::TypeDecl>(rDecl)) {
305 mangleCtx->mangleCXXRTTI(rDecl->getASTContext().getTypeDeclType(TD), sstr);
306 }
307 }
308 if (!mangledName.empty()) {
309 int errDemangle = 0;
310#ifdef WIN32
311 if (mangledName[0] == '\01')
312 mangledName.erase(0, 1);
315 static const char typeinfoNameFor[] = " `RTTI Type Descriptor'";
317 std::string demangledName = demangledTIName;
319#else
322 static const char typeinfoNameFor[] = "typeinfo for ";
325#endif
327 return demangledName;
328 } else {
329#ifdef WIN32
330 ROOT::TMetaUtils::Error("AnnotatedRecordDecl::BuildDemangledTypeInfo",
331 "Demangled typeinfo name '%s' does not contain `RTTI Type Descriptor'\n",
333#else
334 ROOT::TMetaUtils::Error("AnnotatedRecordDecl::BuildDemangledTypeInfo",
335 "Demangled typeinfo name '%s' does not start with 'typeinfo for'\n",
337#endif
338 } // if demangled type_info starts with "typeinfo for "
339 } // if demangling worked
341 } // if mangling worked
342 return {};
343}
344
345
346////////////////////////////////////////////////////////////////////////////////
347/// There is no requested type name.
348/// Still let's normalized the actual name.
349
350// clang-format off
376
377////////////////////////////////////////////////////////////////////////////////
378/// Normalize the requested type name.
379
380// clang-format off
382 const clang::Type *requestedType,
383 const clang::RecordDecl *decl,
384 const char *requestName,
385 unsigned int nTemplateArgsToSkip,
386 bool rStreamerInfo,
387 bool rNoStreamer,
392 const std::string &rRequestedRNTupleSoARecord,
393 const cling::Interpreter &interpreter,
395 : fRuleIndex(index),
396 fDecl(decl),
397 fRequestedName(""),
398 fRequestStreamerInfo(rStreamerInfo),
399 fRequestNoStreamer(rNoStreamer),
400 fRequestNoInputOperator(rRequestNoInputOperator),
401 fRequestOnlyTClass(rRequestOnlyTClass),
402 fRequestedVersionNumber(rRequestVersionNumber),
403 fRequestedRNTupleSerializationMode(rRequestedRNTupleSerializationMode),
404 fRequestedRNTupleSoARecord(rRequestedRNTupleSoARecord)
405// clang-format on
406{
407 // For comparison purposes.
409 splitname1.ShortType(fRequestedName, 0);
410
413 ROOT::TMetaUtils::Warning("AnnotatedRecordDecl",
414 "Could not remove the requested template arguments.\n");
415 }
417}
418
419////////////////////////////////////////////////////////////////////////////////
420/// Normalize the requested type name.
421
422// clang-format off
424 const clang::Type *requestedType,
425 const clang::RecordDecl *decl,
426 const char *requestName,
427 bool rStreamerInfo,
428 bool rNoStreamer,
433 const std::string &rRequestedRNTupleSoARecord,
434 const cling::Interpreter &interpreter,
436 : fRuleIndex(index),
437 fDecl(decl),
438 fRequestedName(""),
439 fRequestStreamerInfo(rStreamerInfo),
440 fRequestNoStreamer(rNoStreamer),
441 fRequestNoInputOperator(rRequestNoInputOperator),
442 fRequestOnlyTClass(rRequestOnlyTClass),
443 fRequestedVersionNumber(rRequestVersionNumber),
444 fRequestedRNTupleSerializationMode(rRequestedRNTupleSerializationMode),
445 fRequestedRNTupleSoARecord(rRequestedRNTupleSoARecord)
446// clang-format on
447{
448 // For comparison purposes.
450 splitname1.ShortType(fRequestedName, 0);
451
454}
455
456////////////////////////////////////////////////////////////////////////////////
457/// Normalize the requested name.
458
459// clang-format off
461 const clang::RecordDecl *decl,
462 const char *requestName,
463 bool rStreamerInfo,
464 bool rNoStreamer,
469 const std::string &rRequestedRNTupleSoARecord,
470 const cling::Interpreter &interpreter,
472 : fRuleIndex(index),
473 fDecl(decl),
474 fRequestedName(""),
475 fRequestStreamerInfo(rStreamerInfo),
476 fRequestNoStreamer(rNoStreamer),
477 fRequestNoInputOperator(rRequestNoInputOperator),
478 fRequestOnlyTClass(rRequestOnlyTClass),
479 fRequestedVersionNumber(rRequestVersionNumber),
480 fRequestedRNTupleSerializationMode(rRequestedRNTupleSerializationMode),
481 fRequestedRNTupleSoARecord(rRequestedRNTupleSoARecord)
482// clang-format on
483{
484 // const clang::ClassTemplateSpecializationDecl *tmplt_specialization = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl> (decl);
485 // if (tmplt_specialization) {
486 // tmplt_specialization->getTemplateArgs ().data()->print(decl->getASTContext().getPrintingPolicy(),llvm::outs());
487 // llvm::outs() << "\n";
488 // }
489 // const char *current = requestName;
490 // Strips spaces and std::
491 if (requestName && requestName[0]) {
494
496 } else {
497 TMetaUtils::GetNormalizedName( fNormalizedName, decl->getASTContext().getCanonicalTagType(decl),interpreter,normCtxt);
498 }
500}
501
502////////////////////////////////////////////////////////////////////////////////
503
505 ExistingTypeCheck_t existingTypeCheck, CheckInClassTable_t CheckInClassTable,
506 AutoParse_t autoParse, bool *shuttingDownPtr, const int *pgDebug /*= 0*/)
507 : fInterpreter(&interpreter),
508 fNormalizedCtxt(&normCtxt),
509 fExistingTypeCheck(existingTypeCheck),
510 fCheckInClassTable(CheckInClassTable),
511 fAutoParse(autoParse),
512 fInterpreterIsShuttingDownPtr(shuttingDownPtr),
513 fPDebug(pgDebug)
514{
515}
516
517////////////////////////////////////////////////////////////////////////////////
518/// Helper routine to ry hard to avoid looking up in the Cling database as
519/// this could enduce an unwanted autoparsing.
520
522 std::string &result)
523{
524 if (tname.empty()) return false;
525
527 else return false;
528}
529
530bool TClingLookupHelper::CheckInClassTable(const std::string &tname, std::string &result)
531{
532 if (tname.empty())
533 return false;
534
537 else
538 return false;
539}
540
541////////////////////////////////////////////////////////////////////////////////
542
544{
545 const cling::LookupHelper& lh = fInterpreter->getLookupHelper();
546 clang::QualType t = lh.findType(nameLong, ToLHDS(WantDiags()));
547 if (!t.isNull()) {
548 clang::QualType dest = cling::utils::Transform::GetPartiallyDesugaredType(fInterpreter->getCI()->getASTContext(), t, fNormalizedCtxt->GetConfig(), true /* fully qualify */);
549 if (!dest.isNull() && (dest != t)) {
550 // getAsStringInternal() appends.
551 nameLong.clear();
552 dest.getAsStringInternal(nameLong, fInterpreter->getCI()->getASTContext().getPrintingPolicy());
553 }
554 }
555}
556
557////////////////////////////////////////////////////////////////////////////////
558
560 const std::string &nameLong)
561{
562 // We are going to use and possibly update the interpreter information.
563 cling::InterpreterAccessRAII LockAccess(*fInterpreter);
564
565 const cling::LookupHelper& lh = fInterpreter->getLookupHelper();
566 clang::QualType t = lh.findType(nondef.c_str(), ToLHDS(WantDiags()));
567 if (!t.isNull()) {
568 clang::QualType dest = cling::utils::Transform::GetPartiallyDesugaredType(fInterpreter->getCI()->getASTContext(), t, fNormalizedCtxt->GetConfig(), true /* fully qualify */);
569 if (!dest.isNull() && (dest != t) &&
570 nameLong == t.getAsString(fInterpreter->getCI()->getASTContext().getPrintingPolicy()))
571 return true;
572 }
573 return false;
574}
575
576////////////////////////////////////////////////////////////////////////////////
577
578bool TClingLookupHelper::IsDeclaredScope(const std::string &base, bool &isInlined)
579{
580 // We are going to use and possibly update the interpreter information.
581 cling::InterpreterAccessRAII LockAccess(*fInterpreter);
582
583 const cling::LookupHelper& lh = fInterpreter->getLookupHelper();
584 const clang::Decl *scope = lh.findScope(base.c_str(), ToLHDS(WantDiags()), nullptr);
585
586 if (!scope) {
587 // the nesting namespace is not declared
588 isInlined = false;
589 return false;
590 }
591 const clang::NamespaceDecl *nsdecl = llvm::dyn_cast<clang::NamespaceDecl>(scope);
592 isInlined = nsdecl && nsdecl->isInline();
593 return true;
594}
595
596////////////////////////////////////////////////////////////////////////////////
597/// We assume that we have a simple type:
598/// [const] typename[*&][const]
599
601 std::string &result,
602 bool dropstd /* = true */)
603{
604 if (tname.empty()) return false;
605
606 // Try hard to avoid looking up in the Cling database as this could enduce
607 // an unwanted autoparsing.
608 // Note: this is always done by the callers and thus is redundant.
609 // Maybe replace with
612 return ! result.empty();
613 }
614
615 if (fAutoParse) fAutoParse(tname.c_str());
616
617 // We are going to use and possibly update the interpreter information.
618 cling::InterpreterAccessRAII LockAccess(*fInterpreter);
619
620 // Since we already check via other means (TClassTable which is populated by
621 // the dictonary loading, and the gROOT list of classes and enums, which are
622 // populated via TProtoClass/Enum), we should be able to disable the autoloading
623 // ... which requires access to libCore or libCling ...
624 const cling::LookupHelper& lh = fInterpreter->getLookupHelper();
625 clang::QualType t = lh.findType(tname.c_str(), ToLHDS(WantDiags()));
626 // Technically we ought to try:
627 // if (t.isNull()) t = lh.findType(TClassEdit::InsertStd(tname), ToLHDS(WantDiags()));
628 // at least until the 'normalized name' contains the std:: prefix.
629
630 if (!t.isNull()) {
632 if (!dest.isNull() && dest != t) {
633 // Since our input is not a template instance name, rather than going through the full
634 // TMetaUtils::GetNormalizedName, we just do the 'strip leading std' and fix
635 // white space.
636 clang::PrintingPolicy policy(fInterpreter->getCI()->getASTContext().getPrintingPolicy());
637 policy.SuppressTagKeyword = true; // Never get the class or struct keyword
638 policy.SuppressTagKeywordInAnonNames = true; // Skip printing tags for anonymous entities
639 // The scope suppression is required for getting rid of the anonymous part of the name of a class defined in an
640 // anonymous namespace. In LLVM22 (and before), SuppressUnwrittenScope suppresses anonymous namespaces. Inline
641 // namespace suppression is separately controlled by SuppressInlineNamespace, which we probably don't want to
642 // be suppressed.
643 policy.SuppressUnwrittenScope = true; // Strip anonymous namespace names
644
645 // getAsStringInternal() appends.
646 result.clear();
647 dest.getAsStringInternal(result, policy);
648 // Strip the std::
649 unsigned long offset = 0;
650 if (strncmp(result.c_str(), "const ", 6) == 0) {
651 offset = 6;
652 }
653 if (dropstd && strncmp(result.c_str()+offset, "std::", 5) == 0) {
654 result.erase(offset,5);
655 }
656 for(unsigned int i = 1; i<result.length(); ++i) {
657 if (result[i]=='s') {
658 if (result[i-1]=='<' || result[i-1]==',' || result[i-1]==' ') {
659 if (dropstd && result.compare(i,5,"std::",5) == 0) {
660 result.erase(i,5);
661 }
662 }
663 }
664 if (result[i]==' ') {
665 if (result[i-1] == ',') {
666 result.erase(i,1);
667 --i;
668 } else if ( (i+1) < result.length() &&
669 (result[i+1]=='*' || result[i+1]=='&' || result[i+1]=='[') ) {
670 result.erase(i,1);
671 --i;
672 }
673 }
674 }
675
676// std::string alt;
677// TMetaUtils::GetNormalizedName(alt, dest, *fInterpreter, *fNormalizedCtxt);
678// if (alt != result) fprintf(stderr,"norm: %s vs result=%s\n",alt.c_str(),result.c_str());
679
680 return true;
681 }
682
683 // LLVM22: ElaboratedType comparison (dest != t) changed due to
684 // https://github.com/llvm/llvm-project/pull/147835
685 // There is no elaborated type and the codepath above is not taken
686 // for previous elaborated types where we need the enum/class/struct
687 // keywords to be stripped.
688
689 // TODO: Remove this comment once we upstream this.
690 // For types like "enum CustomEnum", SuppressTagKeyword no longer
691 // helps (unless we patch clang) because the keyword is printed
692 // unconditionally in the non-canonical elaborated path.
693 // We are using a patched clang, but we still need to handle this
694 // separately for windows.
695 if (!dest.isNull()) {
696 for (std::string_view kw : {"class ", "struct ", "enum "}) {
697 if (tname.compare(0, kw.size(), kw) == 0) {
698 result = tname.substr(kw.size());
699 return true;
700 }
701 }
702 }
703 }
704 return false;
705}
706
707////////////////////////////////////////////////////////////////////////////////
708// TClassEdit will call this routine as soon as any of its static variable (used
709// for caching) is destroyed.
715
716 } // end namespace ROOT
717} // end namespace TMetaUtils
718
719
720////////////////////////////////////////////////////////////////////////////////
721/// Insert the type with name into the collection of typedefs to keep.
722/// if replace, replace occurrences of the canonical type by name.
723
725 const char* name,
726 bool replace /*=false*/) {
727 clang::QualType toSkip = lh.findType(name, cling::LookupHelper::WithDiagnostics);
728 if (const clang::Type* T = toSkip.getTypePtr()) {
729 const clang::TypedefType *tt = llvm::dyn_cast<clang::TypedefType>(T);
730 if (!tt) return;
731 clang::Decl* D = tt->getDecl();
732 fConfig.m_toSkip.insert(D);
733 if (replace) {
734 clang::QualType canon = toSkip->getCanonicalTypeInternal();
735 fConfig.m_toReplace.insert(std::make_pair(canon.getTypePtr(),T));
736 } else {
737 fTypeWithAlternative.insert(T);
738 }
739 }
740}
741
742////////////////////////////////////////////////////////////////////////////////
743/// Initialize the list of typedef to keep (i.e. make them opaque for normalization)
744/// and the list of typedef whose semantic is different from their underlying type
745/// (Double32_t and Float16_t).
746/// This might be specific to an interpreter.
747
749{
750 keepTypedef(lh, "Double32_t");
751 keepTypedef(lh, "Float16_t");
752 keepTypedef(lh, "Long64_t", true);
753 keepTypedef(lh, "ULong64_t", true);
754
755 clang::QualType toSkip = lh.findType("string", cling::LookupHelper::WithDiagnostics);
756 if (!toSkip.isNull()) {
757 if (const clang::TypedefType* TT
758 = llvm::dyn_cast_or_null<clang::TypedefType>(toSkip.getTypePtr()))
759 fConfig.m_toSkip.insert(TT->getDecl());
760 }
761 toSkip = lh.findType("std::string", cling::LookupHelper::WithDiagnostics);
762 if (!toSkip.isNull()) {
763 if (const clang::TypedefType* TT
764 = llvm::dyn_cast_or_null<clang::TypedefType>(toSkip.getTypePtr()))
765 fConfig.m_toSkip.insert(TT->getDecl());
766
767 clang::QualType canon = toSkip->getCanonicalTypeInternal();
768 fConfig.m_toReplace.insert(std::make_pair(canon.getTypePtr(),toSkip.getTypePtr()));
769 }
770}
771
774
775////////////////////////////////////////////////////////////////////////////////
776
777inline bool IsTemplate(const clang::Decl &cl)
778{
779 return (cl.getKind() == clang::Decl::ClassTemplatePartialSpecialization
780 || cl.getKind() == clang::Decl::ClassTemplateSpecialization);
781}
782
783
784////////////////////////////////////////////////////////////////////////////////
785
786const clang::FunctionDecl* ROOT::TMetaUtils::ClassInfo__HasMethod(const clang::DeclContext *cl, const char* name,
787 const cling::Interpreter& interp)
788{
789 clang::Sema* S = &interp.getSema();
790 const clang::NamedDecl* ND = cling::utils::Lookup::Named(S, name, cl);
791 if (ND == (clang::NamedDecl*)-1)
792 return (clang::FunctionDecl*)-1;
793 return llvm::dyn_cast_or_null<clang::FunctionDecl>(ND);
794}
795
796////////////////////////////////////////////////////////////////////////////////
797/// Return the scope corresponding to 'name' or std::'name'
798
799const clang::CXXRecordDecl *
800ROOT::TMetaUtils::ScopeSearch(const char *name, const cling::Interpreter &interp,
801 bool /*diagnose*/, const clang::Type** resultType)
802{
803 const cling::LookupHelper& lh = interp.getLookupHelper();
804 // We have many bogus diagnostics if we allow diagnostics here. Suppress.
805 // FIXME: silence them in the callers.
806 const clang::CXXRecordDecl *result
807 = llvm::dyn_cast_or_null<clang::CXXRecordDecl>
808 (lh.findScope(name, cling::LookupHelper::NoDiagnostics, resultType));
809 if (!result) {
810 std::string std_name("std::");
811 std_name += name;
812 // We have many bogus diagnostics if we allow diagnostics here. Suppress.
813 // FIXME: silence them in the callers.
814 result = llvm::dyn_cast_or_null<clang::CXXRecordDecl>
815 (lh.findScope(std_name, cling::LookupHelper::NoDiagnostics, resultType));
816 }
817 return result;
818}
819
820
821////////////////////////////////////////////////////////////////////////////////
822
823bool ROOT::TMetaUtils::RequireCompleteType(const cling::Interpreter &interp, const clang::CXXRecordDecl *cl)
824{
825 clang::QualType qType = cl->getASTContext().getCanonicalTagType(cl);
826 return RequireCompleteType(interp,cl->getLocation(),qType);
827}
828
829////////////////////////////////////////////////////////////////////////////////
830
831bool ROOT::TMetaUtils::RequireCompleteType(const cling::Interpreter &interp, clang::SourceLocation Loc, clang::QualType Type)
832{
833 clang::Sema& S = interp.getCI()->getSema();
834 // Here we might not have an active transaction to handle
835 // the caused instantiation decl.
836 cling::Interpreter::PushTransactionRAII RAII(const_cast<cling::Interpreter*>(&interp));
837 return S.RequireCompleteType(Loc, Type, clang::diag::err_incomplete_type);
838}
839
840////////////////////////////////////////////////////////////////////////////////
841
842bool ROOT::TMetaUtils::IsBase(const clang::CXXRecordDecl *cl, const clang::CXXRecordDecl *base,
843 const clang::CXXRecordDecl *context, const cling::Interpreter &interp)
844{
845 if (!cl || !base) {
846 return false;
847 }
848
849 if (!cl->getDefinition() || !cl->isCompleteDefinition()) {
851 }
852
853 if (!CheckDefinition(cl, context) || !CheckDefinition(base, context)) {
854 return false;
855 }
856
857 if (!base->hasDefinition()) {
858 ROOT::TMetaUtils::Error("IsBase", "Missing definition for class %s\n", base->getName().str().c_str());
859 return false;
860 }
861 return cl->isDerivedFrom(base);
862}
863
864////////////////////////////////////////////////////////////////////////////////
865
866bool ROOT::TMetaUtils::IsBase(const clang::FieldDecl &m, const char* basename, const cling::Interpreter &interp)
867{
868 const clang::CXXRecordDecl* CRD = llvm::dyn_cast<clang::CXXRecordDecl>(ROOT::TMetaUtils::GetUnderlyingRecordDecl(m.getType()));
869 if (!CRD) {
870 return false;
871 }
872
873 const clang::NamedDecl *base
874 = ScopeSearch(basename, interp, true /*diagnose*/, nullptr);
875
876 if (base) {
877 return IsBase(CRD, llvm::dyn_cast<clang::CXXRecordDecl>( base ),
878 llvm::dyn_cast<clang::CXXRecordDecl>(m.getDeclContext()),interp);
879 }
880 return false;
881}
882
883////////////////////////////////////////////////////////////////////////////////
884
886 const clang::NamedDecl &forcontext,
887 const clang::QualType &qti,
888 const char *R__t,int rwmode,
889 const cling::Interpreter &interp,
890 const char *tcl)
891{
892 static const clang::CXXRecordDecl *TObject_decl
893 = ROOT::TMetaUtils::ScopeSearch("TObject", interp, true /*diag*/, nullptr);
894 enum {
895 kBIT_ISTOBJECT = 0x10000000,
896 kBIT_HASSTREAMER = 0x20000000,
897 kBIT_ISSTRING = 0x40000000,
898
899 kBIT_ISPOINTER = 0x00001000,
900 kBIT_ISFUNDAMENTAL = 0x00000020,
901 kBIT_ISENUM = 0x00000008
902 };
903
904 const clang::Type &ti( * qti.getTypePtr() );
905 std::string tiName;
907
908 std::string objType(ROOT::TMetaUtils::ShortTypeName(tiName.c_str()));
909
910 const clang::Type *rawtype = ROOT::TMetaUtils::GetUnderlyingType(clang::QualType(&ti,0));
911 std::string rawname;
913
914 clang::CXXRecordDecl *cxxtype = rawtype->getAsCXXRecordDecl() ;
916 int isTObj = cxxtype && (IsBase(cxxtype,TObject_decl,nullptr,interp) || rawname == "TObject");
917
918 long kase = 0;
919
920 if (ti.isPointerType()) kase |= kBIT_ISPOINTER;
921 if (rawtype->isFundamentalType()) kase |= kBIT_ISFUNDAMENTAL;
922 if (rawtype->isEnumeralType()) kase |= kBIT_ISENUM;
923
924
925 if (isTObj) kase |= kBIT_ISTOBJECT;
927 if (tiName == "string") kase |= kBIT_ISSTRING;
928 if (tiName == "string*") kase |= kBIT_ISSTRING;
929
930
931 if (!tcl)
932 tcl = " internal error in rootcling ";
933 // if (strcmp(objType,"string")==0) RStl::Instance().GenerateTClassFor( "string", interp, normCtxt );
934
935 if (rwmode == 0) { //Read mode
936
937 if (R__t) finalString << " " << tiName << " " << R__t << ";" << std::endl;
938 switch (kase) {
939
941 if (!R__t) return 0;
942 finalString << " R__b >> " << R__t << ";" << std::endl;
943 break;
944
946 if (!R__t) return 1;
947 finalString << " " << R__t << " = (" << tiName << ")R__b.ReadObjectAny(" << tcl << ");" << std::endl;
948 break;
949
950 case kBIT_ISENUM:
951 if (!R__t) return 0;
952 // fprintf(fp, " R__b >> (Int_t&)%s;\n",R__t);
953 // On some platforms enums and not 'Int_t' and casting to a reference to Int_t
954 // induces the silent creation of a temporary which is 'filled' __instead of__
955 // the desired enum. So we need to take it one step at a time.
956 finalString << " Int_t readtemp;" << std::endl
957 << " R__b >> readtemp;" << std::endl
958 << " " << R__t << " = static_cast<" << tiName << ">(readtemp);" << std::endl;
959 break;
960
961 case kBIT_HASSTREAMER:
963 if (!R__t) return 0;
964 finalString << " " << R__t << ".Streamer(R__b);" << std::endl;
965 break;
966
968 if (!R__t) return 1;
969 //fprintf(fp, " fprintf(stderr,\"info is %%p %%d\\n\",R__b.GetInfo(),R__b.GetInfo()?R__b.GetInfo()->GetOldVersion():-1);\n");
970 finalString << " if (R__b.GetInfo() && R__b.GetInfo()->GetOldVersion()<=3) {" << std::endl;
971 if (cxxtype && cxxtype->isAbstract()) {
972 finalString << " R__ASSERT(0);// " << objType << " is abstract. We assume that older file could not be produced using this streaming method." << std::endl;
973 } else {
974 finalString << " " << R__t << " = new " << objType << ";" << std::endl
975 << " " << R__t << "->Streamer(R__b);" << std::endl;
976 }
977 finalString << " } else {" << std::endl
978 << " " << R__t << " = (" << tiName << ")R__b.ReadObjectAny(" << tcl << ");" << std::endl
979 << " }" << std::endl;
980 break;
981
982 case kBIT_ISSTRING:
983 if (!R__t) return 0;
984 finalString << " {TString R__str;" << std::endl
985 << " R__str.Streamer(R__b);" << std::endl
986 << " " << R__t << " = R__str.Data();}" << std::endl;
987 break;
988
989 case kBIT_ISSTRING|kBIT_ISPOINTER:
990 if (!R__t) return 0;
991 finalString << " {TString R__str;" << std::endl
992 << " R__str.Streamer(R__b);" << std::endl
993 << " " << R__t << " = new string(R__str.Data());}" << std::endl;
994 break;
995
996 case kBIT_ISPOINTER:
997 if (!R__t) return 1;
998 finalString << " " << R__t << " = (" << tiName << ")R__b.ReadObjectAny(" << tcl << ");" << std::endl;
999 break;
1000
1001 default:
1002 if (!R__t) return 1;
1003 finalString << " R__b.StreamObject(&" << R__t << "," << tcl << ");" << std::endl;
1004 break;
1005 }
1006
1007 } else { //Write case
1008
1009 switch (kase) {
1010
1011 case kBIT_ISFUNDAMENTAL:
1013 if (!R__t) return 0;
1014 finalString << " R__b << " << R__t << ";" << std::endl;
1015 break;
1016
1017 case kBIT_ISENUM:
1018 if (!R__t) return 0;
1019 finalString << " { void *ptr_enum = (void*)&" << R__t << ";\n";
1020 finalString << " R__b >> *reinterpret_cast<Int_t*>(ptr_enum); }" << std::endl;
1021 break;
1022
1023 case kBIT_HASSTREAMER:
1025 if (!R__t) return 0;
1026 finalString << " ((" << objType << "&)" << R__t << ").Streamer(R__b);" << std::endl;
1027 break;
1028
1030 if (!R__t) return 1;
1031 finalString << " R__b.WriteObjectAny(" << R__t << "," << tcl << ");" << std::endl;
1032 break;
1033
1034 case kBIT_ISSTRING:
1035 if (!R__t) return 0;
1036 finalString << " {TString R__str(" << R__t << ".c_str());" << std::endl
1037 << " R__str.Streamer(R__b);};" << std::endl;
1038 break;
1039
1040 case kBIT_ISSTRING|kBIT_ISPOINTER:
1041 if (!R__t) return 0;
1042 finalString << " {TString R__str(" << R__t << "->c_str());" << std::endl
1043 << " R__str.Streamer(R__b);}" << std::endl;
1044 break;
1045
1046 case kBIT_ISPOINTER:
1047 if (!R__t) return 1;
1048 finalString << " R__b.WriteObjectAny(" << R__t << "," << tcl <<");" << std::endl;
1049 break;
1050
1051 default:
1052 if (!R__t) return 1;
1053 finalString << " R__b.StreamObject((" << objType << "*)&" << R__t << "," << tcl << ");" << std::endl;
1054 break;
1055 }
1056 }
1057 return 0;
1058}
1059
1060////////////////////////////////////////////////////////////////////////////////
1061/// Checks if default constructor exists and accessible
1062
1063bool ROOT::TMetaUtils::CheckDefaultConstructor(const clang::CXXRecordDecl* cl, const cling::Interpreter& interpreter)
1064{
1065 clang::CXXRecordDecl* ncCl = const_cast<clang::CXXRecordDecl*>(cl);
1066
1067 // We may induce template instantiation
1068 cling::Interpreter::PushTransactionRAII clingRAII(const_cast<cling::Interpreter*>(&interpreter));
1069
1070 if (auto* Ctor = interpreter.getCI()->getSema().LookupDefaultConstructor(ncCl)) {
1071 if (Ctor->getAccess() == clang::AS_public && !Ctor->isDeleted()) {
1072 return true;
1073 }
1074 }
1075
1076 return false;
1077}
1078
1079
1080////////////////////////////////////////////////////////////////////////////////
1081/// Checks IO constructor - must be public and with specified argument
1082
1084 const char *typeOfArg,
1085 const clang::CXXRecordDecl *expectedArgType,
1086 const cling::Interpreter& interpreter)
1087{
1088 if (typeOfArg && !expectedArgType) {
1089 const cling::LookupHelper& lh = interpreter.getLookupHelper();
1090 // We can not use findScope since the type we are given are usually,
1091 // only forward declared (and findScope explicitly reject them).
1092 clang::QualType instanceType = lh.findType(typeOfArg, cling::LookupHelper::WithDiagnostics);
1093 if (!instanceType.isNull())
1094 expectedArgType = instanceType->getAsCXXRecordDecl();
1095 }
1096
1097 if (!expectedArgType)
1098 return EIOCtorCategory::kAbsent;
1099
1100 // FIXME: We should not iterate here. That costs memory!
1101 cling::Interpreter::PushTransactionRAII clingRAII(const_cast<cling::Interpreter*>(&interpreter));
1102 for (auto iter = cl->ctor_begin(), end = cl->ctor_end(); iter != end; ++iter)
1103 {
1104 if ((iter->getAccess() != clang::AS_public) || (iter->getNumParams() != 1))
1105 continue;
1106
1107 // We can reach this constructor.
1108 clang::QualType argType((*iter->param_begin())->getType());
1109 argType = argType.getDesugaredType(cl->getASTContext());
1110 // Deal with pointers and references: ROOT-7723
1111 auto ioCtorCategory = EIOCtorCategory::kAbsent;
1112 if (argType->isPointerType()) {
1113 ioCtorCategory = EIOCtorCategory::kIOPtrType;
1114 argType = argType->getPointeeType();
1115 } else if (argType->isReferenceType()) {
1116 ioCtorCategory = EIOCtorCategory::kIORefType;
1117 argType = argType.getNonReferenceType();
1118 } else
1119 continue;
1120
1121 argType = argType.getDesugaredType(cl->getASTContext());
1122 const clang::CXXRecordDecl *argDecl = argType->getAsCXXRecordDecl();
1123 if (argDecl) {
1124 if (argDecl->getCanonicalDecl() == expectedArgType->getCanonicalDecl()) {
1125 return ioCtorCategory;
1126 }
1127 } else {
1128 std::string realArg = argType.getAsString();
1129 std::string clarg("class ");
1130 clarg += typeOfArg;
1131 if (realArg == clarg)
1132 return ioCtorCategory;
1133 }
1134 } // for each constructor
1135
1136 return EIOCtorCategory::kAbsent;
1137}
1138
1139
1140////////////////////////////////////////////////////////////////////////////////
1141/// Check if class has constructor of provided type - either default or with single argument
1142
1145 const cling::Interpreter& interpreter)
1146{
1147 const char *arg = ioctortype.GetName();
1148
1149 if (!ioctortype.GetType() && (!arg || !arg[0])) {
1150 // We are looking for a constructor with zero non-default arguments.
1151
1152 return CheckDefaultConstructor(cl, interpreter) ? EIOCtorCategory::kDefault : EIOCtorCategory::kAbsent;
1153 }
1154
1155 return CheckIOConstructor(cl, arg, ioctortype.GetType(), interpreter);
1156}
1157
1158
1159////////////////////////////////////////////////////////////////////////////////
1160
1161const clang::CXXMethodDecl *GetMethodWithProto(const clang::Decl* cinfo,
1162 const char *method, const char *proto,
1163 const cling::Interpreter &interp,
1164 bool diagnose)
1165{
1166 const clang::FunctionDecl* funcD
1167 = interp.getLookupHelper().findFunctionProto(cinfo, method, proto,
1168 diagnose ? cling::LookupHelper::WithDiagnostics
1169 : cling::LookupHelper::NoDiagnostics);
1170 if (funcD)
1171 return llvm::dyn_cast<const clang::CXXMethodDecl>(funcD);
1172
1173 return nullptr;
1174}
1175
1176
1177////////////////////////////////////////////////////////////////////////////////
1178
1179namespace ROOT {
1180 namespace TMetaUtils {
1181 RConstructorType::RConstructorType(const char *type_of_arg, const cling::Interpreter &interp) : fArgTypeName(type_of_arg),fArgType(nullptr)
1182 {
1183 const cling::LookupHelper& lh = interp.getLookupHelper();
1184 // We can not use findScope since the type we are given are usually,
1185 // only forward declared (and findScope explicitly reject them).
1186 clang::QualType instanceType = lh.findType(type_of_arg, cling::LookupHelper::WithDiagnostics);
1187 if (!instanceType.isNull())
1188 fArgType = instanceType->getAsCXXRecordDecl();
1189 }
1190 const char *RConstructorType::GetName() const { return fArgTypeName.c_str(); }
1191 const clang::CXXRecordDecl *RConstructorType::GetType() const { return fArgType; }
1192 }
1193}
1194
1195////////////////////////////////////////////////////////////////////////////////
1196/// return true if we can find an constructor calleable without any arguments
1197/// or with one the IOCtor special types.
1198
1199bool ROOT::TMetaUtils::HasIOConstructor(const clang::CXXRecordDecl *cl,
1200 std::string& arg,
1202 const cling::Interpreter &interp)
1203{
1204 if (cl->isAbstract()) return false;
1205
1206 for (auto & ctorType : ctorTypes) {
1207
1209
1210 if (EIOCtorCategory::kAbsent == ioCtorCat)
1211 continue;
1212
1213 std::string proto( ctorType.GetName() );
1214 bool defaultCtor = proto.empty();
1215 if (defaultCtor) {
1216 arg.clear();
1217 } else {
1218 // I/O constructors can take pointers or references to ctorTypes
1219 proto += " *";
1220 if (EIOCtorCategory::kIOPtrType == ioCtorCat) {
1221 arg = "( ("; //(MyType*)nullptr
1222 } else if (EIOCtorCategory::kIORefType == ioCtorCat) {
1223 arg = "( *("; //*(MyType*)nullptr
1224 }
1225 arg += proto;
1226 arg += ")nullptr )";
1227 }
1228 // Check for private operator new
1229 const clang::CXXMethodDecl *method
1230 = GetMethodWithProto(cl, "operator new", "size_t", interp,
1231 cling::LookupHelper::NoDiagnostics);
1232 if (method && method->getAccess() != clang::AS_public) {
1233 // The non-public op new is not going to improve for other c'tors.
1234 return false;
1235 }
1236
1237 // This one looks good!
1238 return true;
1239 }
1240 return false;
1241}
1242
1243////////////////////////////////////////////////////////////////////////////////
1244
1245bool ROOT::TMetaUtils::NeedDestructor(const clang::CXXRecordDecl *cl,
1246 const cling::Interpreter& interp)
1247{
1248 if (!cl) return false;
1249
1250 if (cl->hasUserDeclaredDestructor()) {
1251
1252 cling::Interpreter::PushTransactionRAII clingRAII(const_cast<cling::Interpreter*>(&interp));
1253 clang::CXXDestructorDecl *dest = cl->getDestructor();
1254 if (dest) {
1255 return (dest->getAccess() == clang::AS_public);
1256 } else {
1257 return true; // no destructor, so let's assume it means default?
1258 }
1259 }
1260 return true;
1261}
1262
1263////////////////////////////////////////////////////////////////////////////////
1264/// Return true, if the function (defined by the name and prototype) exists and is public
1265
1266bool ROOT::TMetaUtils::CheckPublicFuncWithProto(const clang::CXXRecordDecl *cl,
1267 const char *methodname,
1268 const char *proto,
1269 const cling::Interpreter &interp,
1270 bool diagnose)
1271{
1272 const clang::CXXMethodDecl *method
1274 diagnose ? cling::LookupHelper::WithDiagnostics
1275 : cling::LookupHelper::NoDiagnostics);
1276 return (method && method->getAccess() == clang::AS_public);
1277}
1278
1279////////////////////////////////////////////////////////////////////////////////
1280/// Return true if the class has a method DirectoryAutoAdd(TDirectory *)
1281
1282bool ROOT::TMetaUtils::HasDirectoryAutoAdd(const clang::CXXRecordDecl *cl, const cling::Interpreter &interp)
1283{
1284 // Detect if the class has a DirectoryAutoAdd
1285
1286 // Detect if the class or one of its parent has a DirectoryAutoAdd
1287 const char *proto = "TDirectory*";
1288 const char *name = "DirectoryAutoAdd";
1289
1290 return CheckPublicFuncWithProto(cl,name,proto,interp, false /*diags*/);
1291}
1292
1293
1294////////////////////////////////////////////////////////////////////////////////
1295/// Return true if the class has a method Merge(TCollection*,TFileMergeInfo*)
1296
1297bool ROOT::TMetaUtils::HasNewMerge(const clang::CXXRecordDecl *cl, const cling::Interpreter &interp)
1298{
1299 // Detect if the class has a 'new' Merge function.
1300
1301 // Detect if the class or one of its parent has a DirectoryAutoAdd
1302 const char *proto = "TCollection*,TFileMergeInfo*";
1303 const char *name = "Merge";
1304
1305 return CheckPublicFuncWithProto(cl,name,proto,interp, false /*diags*/);
1306}
1307
1308////////////////////////////////////////////////////////////////////////////////
1309/// Return true if the class has a method Merge(TCollection*)
1310
1311bool ROOT::TMetaUtils::HasOldMerge(const clang::CXXRecordDecl *cl, const cling::Interpreter &interp)
1312{
1313 // Detect if the class has an old fashion Merge function.
1314
1315 // Detect if the class or one of its parent has a DirectoryAutoAdd
1316 const char *proto = "TCollection*";
1317 const char *name = "Merge";
1318
1319 return CheckPublicFuncWithProto(cl,name,proto, interp, false /*diags*/);
1320}
1321
1322
1323////////////////////////////////////////////////////////////////////////////////
1324/// Return true if the class has a method ResetAfterMerge(TFileMergeInfo *)
1325
1326bool ROOT::TMetaUtils::HasResetAfterMerge(const clang::CXXRecordDecl *cl, const cling::Interpreter &interp)
1327{
1328 // Detect if the class has a 'new' Merge function.
1329 // bool hasMethod = cl.HasMethod("DirectoryAutoAdd");
1330
1331 // Detect if the class or one of its parent has a DirectoryAutoAdd
1332 const char *proto = "TFileMergeInfo*";
1333 const char *name = "ResetAfterMerge";
1334
1335 return CheckPublicFuncWithProto(cl,name,proto, interp, false /*diags*/);
1336}
1337
1338
1339////////////////////////////////////////////////////////////////////////////////
1340/// Return true if the class has a custom member function streamer.
1341
1343 const clang::CXXRecordDecl* clxx,
1344 const cling::Interpreter &interp,
1346{
1347 static const char *proto = "TBuffer&";
1348
1349 const clang::CXXMethodDecl *method
1350 = GetMethodWithProto(clxx,"Streamer",proto, interp,
1351 cling::LookupHelper::NoDiagnostics);
1352 const clang::DeclContext *clxx_as_context = llvm::dyn_cast<clang::DeclContext>(clxx);
1353
1354 return (method && method->getDeclContext() == clxx_as_context
1355 && ( cl.RequestNoStreamer() || !cl.RequestStreamerInfo()));
1356}
1357
1358////////////////////////////////////////////////////////////////////////////////
1359/// Return true if the class has a custom member function streamer.
1360
1362 const clang::CXXRecordDecl* clxx,
1363 const cling::Interpreter &interp,
1365{
1366 static const char *proto = "TBuffer&,TClass*";
1367
1368 const clang::CXXMethodDecl *method
1369 = GetMethodWithProto(clxx,"Streamer",proto, interp,
1370 cling::LookupHelper::NoDiagnostics);
1371 const clang::DeclContext *clxx_as_context = llvm::dyn_cast<clang::DeclContext>(clxx);
1372
1373 return (method && method->getDeclContext() == clxx_as_context
1374 && ( cl.RequestNoStreamer() || !cl.RequestStreamerInfo()));
1375}
1376
1377
1378////////////////////////////////////////////////////////////////////////////////
1379/// Main implementation relying on GetFullyQualifiedTypeName
1380/// All other GetQualifiedName functions leverage this one except the
1381/// one for namespaces.
1382
1383void ROOT::TMetaUtils::GetQualifiedName(std::string &qual_name, const clang::QualType &type, const clang::NamedDecl &forcontext)
1384{
1386}
1387
1388//----
1389std::string ROOT::TMetaUtils::GetQualifiedName(const clang::QualType &type, const clang::NamedDecl &forcontext)
1390{
1391 std::string result;
1393 type,
1394 forcontext);
1395 return result;
1396}
1397
1398
1399////////////////////////////////////////////////////////////////////////////////
1400
1401void ROOT::TMetaUtils::GetQualifiedName(std::string& qual_type, const clang::Type &type, const clang::NamedDecl &forcontext)
1402{
1403 clang::QualType qualType(&type,0);
1405 qualType,
1406 forcontext);
1407}
1408
1409//---
1410std::string ROOT::TMetaUtils::GetQualifiedName(const clang::Type &type, const clang::NamedDecl &forcontext)
1411{
1412 std::string result;
1414 type,
1415 forcontext);
1416 return result;
1417}
1418
1419// //______________________________________________________________________________
1420// void ROOT::TMetaUtils::GetQualifiedName(std::string &qual_name, const clang::NamespaceDecl &cl)
1421// {
1422// GetQualifiedName(qual_name,cl);
1423// }
1424//
1425// //----
1426// std::string ROOT::TMetaUtils::GetQualifiedName(const clang::NamespaceDecl &cl){
1427// return GetQualifiedName(cl);
1428// }
1429
1430////////////////////////////////////////////////////////////////////////////////
1431/// This implementation does not rely on GetFullyQualifiedTypeName
1432
1433void ROOT::TMetaUtils::GetQualifiedName(std::string &qual_name, const clang::NamedDecl &cl)
1434{
1435 llvm::raw_string_ostream stream(qual_name);
1436 clang::PrintingPolicy policy( cl.getASTContext().getPrintingPolicy() );
1437 policy.SuppressTagKeyword = true; // Never get the class or struct keyword
1438 policy.SuppressTagKeywordInAnonNames = true; // Skip printing tags for anonymous entities
1439 policy.SuppressUnwrittenScope = true; // Don't write the inline or anonymous namespace names.
1440
1441 cl.getNameForDiagnostic(stream,policy,true);
1442 stream.flush(); // flush to string.
1443
1444 if ( qual_name == "(anonymous " || qual_name == "(unnamed" ) {
1445 size_t pos = qual_name.find(':');
1446 qual_name.erase(0,pos+2);
1447 }
1448}
1449
1450//----
1451std::string ROOT::TMetaUtils::GetQualifiedName(const clang::NamedDecl &cl){
1452 std::string result;
1454 return result;
1455}
1456
1457
1458////////////////////////////////////////////////////////////////////////////////
1459
1460void ROOT::TMetaUtils::GetQualifiedName(std::string &qual_name, const clang::RecordDecl &recordDecl)
1461{
1462 clang::QualType qualType = recordDecl.getASTContext().getCanonicalTagType(&recordDecl);
1464 qualType,
1465 recordDecl);
1466}
1467
1468//----
1469std::string ROOT::TMetaUtils::GetQualifiedName(const clang::RecordDecl &recordDecl)
1470{
1471 std::string result;
1473 return result;
1474}
1475
1476////////////////////////////////////////////////////////////////////////////////
1477
1482
1483//----
1490
1491////////////////////////////////////////////////////////////////////////////////
1492/// Create the data member name-type map for given class
1493
1494static void CreateNameTypeMap(const clang::CXXRecordDecl &cl, ROOT::MembersTypeMap_t& nameType)
1495{
1496 std::stringstream dims;
1497 std::string typenameStr;
1498
1499 const clang::ASTContext& astContext = cl.getASTContext();
1500
1501 // Loop over the non static data member.
1502 for(clang::RecordDecl::field_iterator field_iter = cl.field_begin(), end = cl.field_end();
1503 field_iter != end;
1504 ++field_iter){
1505 // The CINT based code was filtering away static variables (they are not part of
1506 // the list starting with field_begin in clang), and const enums (which should
1507 // also not be part of this list).
1508 // It was also filtering out the 'G__virtualinfo' artificial member.
1509
1510 typenameStr.clear();
1511 dims.str("");
1512 dims.clear();
1513
1514 clang::QualType fieldType(field_iter->getType());
1515 if (fieldType->isConstantArrayType()) {
1516 const clang::ConstantArrayType *arrayType = llvm::dyn_cast<clang::ConstantArrayType>(fieldType.getTypePtr());
1517 while (arrayType) {
1518 dims << "[" << arrayType->getSize().getLimitedValue() << "]";
1519 fieldType = arrayType->getElementType();
1520 arrayType = llvm::dyn_cast<clang::ConstantArrayType>(arrayType->getArrayElementTypeNoTypeQual());
1521 }
1522 }
1523
1525 nameType[field_iter->getName().str()] = ROOT::Internal::TSchemaType(typenameStr.c_str(),dims.str().c_str());
1526 }
1527
1528 // And now the base classes
1529 // We also need to look at the base classes.
1530 for(clang::CXXRecordDecl::base_class_const_iterator iter = cl.bases_begin(), end = cl.bases_end();
1531 iter != end;
1532 ++iter){
1533 std::string basename( iter->getType()->getAsCXXRecordDecl()->getNameAsString() ); // Intentionally using only the unqualified name.
1535 }
1536}
1537
1538////////////////////////////////////////////////////////////////////////////////
1539
1540const clang::FunctionDecl *ROOT::TMetaUtils::GetFuncWithProto(const clang::Decl* cinfo,
1541 const char *method,
1542 const char *proto,
1543 const cling::Interpreter &interp,
1544 bool diagnose)
1545{
1546 return interp.getLookupHelper().findFunctionProto(cinfo, method, proto,
1547 diagnose ? cling::LookupHelper::WithDiagnostics
1548 : cling::LookupHelper::NoDiagnostics);
1549}
1550
1551////////////////////////////////////////////////////////////////////////////////
1552/// It looks like the template specialization decl actually contains _less_ information
1553/// on the location of the code than the decl (in case where there is forward declaration,
1554/// that is what the specialization points to.
1555///
1556/// const clang::CXXRecordDecl* clxx = llvm::dyn_cast<clang::CXXRecordDecl>(decl);
1557/// if (clxx) {
1558/// switch(clxx->getTemplateSpecializationKind()) {
1559/// case clang::TSK_Undeclared:
1560/// // We want the default behavior
1561/// break;
1562/// case clang::TSK_ExplicitInstantiationDeclaration:
1563/// case clang::TSK_ExplicitInstantiationDefinition:
1564/// case clang::TSK_ImplicitInstantiation: {
1565/// // We want the location of the template declaration:
1566/// const clang::ClassTemplateSpecializationDecl *tmplt_specialization = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl> (clxx);
1567/// if (tmplt_specialization) {
1568/// return GetLineNumber(const_cast< clang::ClassTemplateSpecializationDecl *>(tmplt_specialization)->getSpecializedTemplate());
1569/// }
1570/// break;
1571/// }
1572/// case clang::TSK_ExplicitSpecialization:
1573/// // We want the default behavior
1574/// break;
1575/// default:
1576/// break;
1577/// }
1578/// }
1579
1581{
1582 clang::SourceLocation sourceLocation = decl->getLocation();
1583 clang::SourceManager& sourceManager = decl->getASTContext().getSourceManager();
1584
1585 if (!sourceLocation.isValid() ) {
1586 return -1;
1587 }
1588
1589 if (!sourceLocation.isFileID()) {
1590 sourceLocation = sourceManager.getExpansionRange(sourceLocation).getEnd();
1591 }
1592
1593 if (sourceLocation.isValid() && sourceLocation.isFileID()) {
1594 return sourceManager.getLineNumber(sourceManager.getFileID(sourceLocation),sourceManager.getFileOffset(sourceLocation));
1595 }
1596 else {
1597 return -1;
1598 }
1599}
1600
1601////////////////////////////////////////////////////////////////////////////////
1602/// Return true if the type is a Double32_t or Float16_t or
1603/// is a instance template that depends on Double32_t or Float16_t.
1604
1606{
1607 while (llvm::isa<clang::PointerType>(instanceType.getTypePtr())
1608 || llvm::isa<clang::ReferenceType>(instanceType.getTypePtr()))
1609 {
1610 instanceType = instanceType->getPointeeType();
1611 }
1612
1613 // There is no typedef to worried about, except for the opaque ones.
1614
1615 // Technically we should probably used our own list with just
1616 // Double32_t and Float16_t
1617 if (normCtxt.GetTypeWithAlternative().count(instanceType.getTypePtr())) {
1618 return true;
1619 }
1620
1621
1622 bool result = false;
1623 const clang::CXXRecordDecl* clxx = instanceType->getAsCXXRecordDecl();
1624 if (clxx && clxx->getTemplateSpecializationKind() != clang::TSK_Undeclared) {
1625 // do the template thing.
1626 const clang::TemplateSpecializationType* TST
1627 = llvm::dyn_cast<const clang::TemplateSpecializationType>(instanceType.getTypePtr());
1628 if (!TST) {
1629 // std::string type_name;
1630 // type_name = GetQualifiedName( instanceType, *clxx );
1631 // fprintf(stderr,"ERROR: Could not findS TST for %s\n",type_name.c_str());
1632 return false;
1633 }
1634 for (const clang::TemplateArgument &TA : TST->template_arguments()) {
1635 if (TA.getKind() == clang::TemplateArgument::Type) {
1637 }
1638 }
1639 }
1640 return result;
1641}
1642
1643////////////////////////////////////////////////////////////////////////////////
1644/// Return true if any of the argument is or contains a double32.
1645
1647 const cling::Interpreter &interp,
1649{
1650 const clang::CXXRecordDecl *clxx = llvm::dyn_cast<clang::CXXRecordDecl>(cl.GetRecordDecl());
1651 if (!clxx || clxx->getTemplateSpecializationKind() == clang::TSK_Undeclared) return false;
1652
1653 clang::QualType instanceType = interp.getLookupHelper().findType(cl.GetNormalizedName(),
1654 cling::LookupHelper::WithDiagnostics);
1655 if (instanceType.isNull()) {
1656 //Error(0,"Could not find the clang::Type for %s\n",cl.GetNormalizedName());
1657 return false;
1658 }
1659
1661}
1662
1663////////////////////////////////////////////////////////////////////////////////
1664/// Extract attr string
1665
1667{
1668 clang::AnnotateAttr* annAttr = clang::dyn_cast<clang::AnnotateAttr>(attribute);
1669 if (!annAttr) {
1670 //TMetaUtils::Error(0,"Could not cast Attribute to AnnotatedAttribute\n");
1671 return 1;
1672 }
1673 attrString = annAttr->getAnnotation().str();
1674 return 0;
1675}
1676
1677////////////////////////////////////////////////////////////////////////////////
1678
1680{
1681 // if separator found, extract name and value
1682 size_t substrFound (attributeStr.find(propNames::separator));
1683 if (substrFound==std::string::npos) {
1684 //TMetaUtils::Error(0,"Could not find property name-value separator (%s)\n",ROOT::TMetaUtils::PropertyNameValSeparator.c_str());
1685 return 1;
1686 }
1687 size_t EndPart1 = attributeStr.find_first_of(propNames::separator) ;
1688 attrName = attributeStr.substr(0, EndPart1);
1689 const int separatorLength(propNames::separator.size());
1691 return 0;
1692}
1693
1694////////////////////////////////////////////////////////////////////////////////
1695
1696int ROOT::TMetaUtils::extractPropertyNameVal(clang::Attr* attribute, std::string& attrName, std::string& attrValue)
1697{
1698 std::string attrString;
1700 if (0!=ret) return ret;
1702}
1703
1704////////////////////////////////////////////////////////////////////////////////
1705/// This routine counts on the "propName<separator>propValue" format
1706
1708 const std::string& propName,
1709 std::string& propValue)
1710{
1711 for (clang::Decl::attr_iterator attrIt = decl.attr_begin();
1712 attrIt!=decl.attr_end();++attrIt){
1713 clang::AnnotateAttr* annAttr = clang::dyn_cast<clang::AnnotateAttr>(*attrIt);
1714 if (!annAttr) continue;
1715
1716 llvm::StringRef attribute = annAttr->getAnnotation();
1717 std::pair<llvm::StringRef,llvm::StringRef> split = attribute.split(propNames::separator.c_str());
1718 if (split.first != propName.c_str()) continue;
1719 else {
1720 propValue = split.second.str();
1721 return true;
1722 }
1723 }
1724 return false;
1725}
1726
1727////////////////////////////////////////////////////////////////////////////////
1728/// This routine counts on the "propName<separator>propValue" format
1729
1731 const std::string& propName,
1732 int& propValue)
1733{
1734 for (clang::Decl::attr_iterator attrIt = decl.attr_begin();
1735 attrIt!=decl.attr_end();++attrIt){
1736 clang::AnnotateAttr* annAttr = clang::dyn_cast<clang::AnnotateAttr>(*attrIt);
1737 if (!annAttr) continue;
1738
1739 llvm::StringRef attribute = annAttr->getAnnotation();
1740 std::pair<llvm::StringRef,llvm::StringRef> split = attribute.split(propNames::separator.c_str());
1741 if (split.first != propName.c_str()) continue;
1742 else {
1743 return split.second.getAsInteger(10,propValue);
1744 }
1745 }
1746 return false;
1747}
1748
1749////////////////////////////////////////////////////////////////////////////////
1750/// FIXME: a function of 450+ lines!
1751
1753 const AnnotatedRecordDecl &cl,
1754 const clang::CXXRecordDecl *decl,
1755 const cling::Interpreter &interp,
1758 bool& needCollectionProxy)
1759{
1760 std::string classname = TClassEdit::GetLong64_Name(cl.GetNormalizedName());
1761
1762 std::string mappedname;
1763 ROOT::TMetaUtils::GetCppName(mappedname,classname.c_str());
1764 std::string csymbol = classname;
1765 std::string args;
1766
1767 if ( ! TClassEdit::IsStdClass( classname.c_str() ) ) {
1768
1769 // Prefix the full class name with '::' except for the STL
1770 // containers and std::string. This is to request the
1771 // real class instead of the class in the namespace ROOT::Shadow
1772 csymbol.insert(0,"::");
1773 }
1774
1775 int stl = TClassEdit::IsSTLCont(classname);
1776 bool bset = TClassEdit::IsSTLBitset(classname.c_str());
1777
1778 bool isStd = TMetaUtils::IsStdClass(*decl);
1779 const cling::LookupHelper& lh = interp.getLookupHelper();
1780 bool isString = TMetaUtils::IsOfType(*decl,"std::string",lh);
1781
1782 bool isStdNotString = isStd && !isString;
1783
1784 finalString << "namespace ROOT {" << "\n";
1785
1786 if (!ClassInfo__HasMethod(decl,"Dictionary",interp) || IsTemplate(*decl))
1787 {
1788 finalString << " static TClass *" << mappedname.c_str() << "_Dictionary();\n"
1789 << " static void " << mappedname.c_str() << "_TClassManip(TClass*);\n";
1790
1791
1792 }
1793
1794 if (HasIOConstructor(decl, args, ctorTypes, interp)) {
1795 finalString << " static void *new_" << mappedname.c_str() << "(void *p = nullptr);" << "\n";
1796
1797 if (args.size()==0 && NeedDestructor(decl, interp))
1798 {
1799 finalString << " static void *newArray_";
1800 finalString << mappedname.c_str();
1801 finalString << "(Long_t size, void *p);";
1802 finalString << "\n";
1803 }
1804 }
1805
1806 if (NeedDestructor(decl, interp)) {
1807 finalString << " static void delete_" << mappedname.c_str() << "(void *p);" << "\n" << " static void deleteArray_" << mappedname.c_str() << "(void *p);" << "\n" << " static void destruct_" << mappedname.c_str() << "(void *p);" << "\n";
1808 }
1810 finalString << " static void directoryAutoAdd_" << mappedname.c_str() << "(void *obj, TDirectory *dir);" << "\n";
1811 }
1813 finalString << " static void streamer_" << mappedname.c_str() << "(TBuffer &buf, void *obj);" << "\n";
1814 }
1816 finalString << " static void conv_streamer_" << mappedname.c_str() << "(TBuffer &buf, void *obj, const TClass*);" << "\n";
1817 }
1819 finalString << " static Long64_t merge_" << mappedname.c_str() << "(void *obj, TCollection *coll,TFileMergeInfo *info);" << "\n";
1820 }
1822 finalString << " static void reset_" << mappedname.c_str() << "(void *obj, TFileMergeInfo *info);" << "\n";
1823 }
1824
1825 //--------------------------------------------------------------------------
1826 // Check if we have any schema evolution rules for this class
1827 /////////////////////////////////////////////////////////////////////////////
1828
1829 ROOT::SchemaRuleClassMap_t::iterator rulesIt1 = ROOT::gReadRules.find( classname.c_str() );
1830 ROOT::SchemaRuleClassMap_t::iterator rulesIt2 = ROOT::gReadRawRules.find( classname.c_str() );
1831
1833 CreateNameTypeMap( *decl, nameTypeMap ); // here types for schema evo are written
1834
1835 //--------------------------------------------------------------------------
1836 // Process the read rules
1837 /////////////////////////////////////////////////////////////////////////////
1838
1839 if( rulesIt1 != ROOT::gReadRules.end() ) {
1840 int i = 0;
1841 finalString << "\n // Schema evolution read functions\n";
1842 std::list<ROOT::SchemaRuleMap_t>::iterator rIt = rulesIt1->second.fRules.begin();
1843 while (rIt != rulesIt1->second.fRules.end()) {
1844
1845 //--------------------------------------------------------------------
1846 // Check if the rules refer to valid data members
1847 ///////////////////////////////////////////////////////////////////////
1848
1849 std::string error_string;
1851 Warning(nullptr, "%s", error_string.c_str());
1852 rIt = rulesIt1->second.fRules.erase(rIt);
1853 continue;
1854 }
1855
1856 //---------------------------------------------------------------------
1857 // Write the conversion function if necessary
1858 ///////////////////////////////////////////////////////////////////////
1859
1860 if( rIt->find( "code" ) != rIt->end() ) {
1862 }
1863 ++rIt;
1864 }
1865 }
1866
1867
1868
1869
1870 //--------------------------------------------------------------------------
1871 // Process the read raw rules
1872 /////////////////////////////////////////////////////////////////////////////
1873
1874 if( rulesIt2 != ROOT::gReadRawRules.end() ) {
1875 int i = 0;
1876 finalString << "\n // Schema evolution read raw functions\n";
1877 std::list<ROOT::SchemaRuleMap_t>::iterator rIt = rulesIt2->second.fRules.begin();
1878 while (rIt != rulesIt2->second.fRules.end()) {
1879
1880 //--------------------------------------------------------------------
1881 // Check if the rules refer to valid data members
1882 ///////////////////////////////////////////////////////////////////////
1883
1884 std::string error_string;
1886 Warning(nullptr, "%s", error_string.c_str());
1887 rIt = rulesIt2->second.fRules.erase(rIt);
1888 continue;
1889 }
1890
1891 //---------------------------------------------------------------------
1892 // Write the conversion function
1893 ///////////////////////////////////////////////////////////////////////
1894
1895 if( rIt->find( "code" ) == rIt->end() )
1896 continue;
1897
1899 ++rIt;
1900 }
1901 }
1902
1903 finalString << "\n" << " // Function generating the singleton type initializer" << "\n";
1904
1905 finalString << " static TGenericClassInfo *GenerateInitInstanceLocal(const " << csymbol << "*)" << "\n" << " {" << "\n";
1906
1907 finalString << " " << csymbol << " *ptr = nullptr;" << "\n";
1908
1909 //fprintf(fp, " static ::ROOT::ClassInfo< %s > \n",classname.c_str());
1910 if (ClassInfo__HasMethod(decl,"IsA",interp) ) {
1911 finalString << " static ::TVirtualIsAProxy* isa_proxy = new ::TInstrumentedIsAProxy< " << csymbol << " >(nullptr);" << "\n";
1912 }
1913 else {
1914 finalString << " static ::TVirtualIsAProxy* isa_proxy = new ::TIsAProxy(typeid(" << csymbol << "));" << "\n";
1915 }
1916 finalString << " static ::ROOT::TGenericClassInfo " << "\n" << " instance(\"" << classname.c_str() << "\", ";
1917
1918 if (ClassInfo__HasMethod(decl,"Class_Version",interp)) {
1919 finalString << csymbol << "::Class_Version(), ";
1920 } else if (bset) {
1921 finalString << "2, "; // bitset 'version number'
1922 } else if (stl) {
1923 finalString << "-2, "; // "::TStreamerInfo::Class_Version(), ";
1924 } else if( cl.HasClassVersion() ) {
1925 finalString << cl.RequestedVersionNumber() << ", ";
1926 } else { // if (cl_input.RequestStreamerInfo()) {
1927
1928 // Need to find out if the operator>> is actually defined for this class.
1929 static const char *versionFunc = "GetClassVersion";
1930 // int ncha = strlen(classname.c_str())+strlen(versionFunc)+5;
1931 // char *funcname= new char[ncha];
1932 // snprintf(funcname,ncha,"%s<%s >",versionFunc,classname.c_str());
1933 std::string proto = classname + "*";
1934 const clang::Decl* ctxt = llvm::dyn_cast<clang::Decl>((*cl).getDeclContext());
1935 const clang::FunctionDecl *methodinfo
1937 interp, cling::LookupHelper::NoDiagnostics);
1938 // delete [] funcname;
1939
1940 if (methodinfo &&
1941 ROOT::TMetaUtils::GetFileName(*methodinfo, interp).find("Rtypes.h") == llvm::StringRef::npos) {
1942
1943 // GetClassVersion was defined in the header file.
1944 //fprintf(fp, "GetClassVersion((%s *)0x0), ",classname.c_str());
1945 finalString << "GetClassVersion< ";
1946 finalString << classname.c_str();
1947 finalString << " >(), ";
1948 }
1949 //static char temporary[1024];
1950 //sprintf(temporary,"GetClassVersion<%s>( (%s *) 0x0 )",classname.c_str(),classname.c_str());
1951 //fprintf(stderr,"DEBUG: %s has value %d\n",classname.c_str(),(int)G__int(G__calc(temporary)));
1952 }
1953
1954 std::string filename = ROOT::TMetaUtils::GetFileName(*cl, interp);
1955 if (filename.length() > 0) {
1956 for (unsigned int i=0; i<filename.length(); i++) {
1957 if (filename[i]=='\\') filename[i]='/';
1958 }
1959 }
1960 finalString << "\"" << filename << "\", " << ROOT::TMetaUtils::GetLineNumber(cl)
1961 << "," << "\n" << " typeid(" << csymbol
1962 << "), ::ROOT::Internal::DefineBehavior(ptr, ptr)," << "\n" << " ";
1963
1964 if (ClassInfo__HasMethod(decl,"Dictionary",interp) && !IsTemplate(*decl)) {
1965 finalString << "&" << csymbol << "::Dictionary, ";
1966 } else {
1967 finalString << "&" << mappedname << "_Dictionary, ";
1968 }
1969
1970 enum {
1971 TClassTable__kHasCustomStreamerMember = 0x10 // See TClassTable.h
1972 };
1973
1974 Int_t rootflag = cl.RootFlag();
1977 }
1978 finalString << "isa_proxy, " << rootflag << "," << "\n"
1979 << " sizeof(" << csymbol << "), alignof(" << csymbol << ") );" << "\n";
1980 if (HasIOConstructor(decl, args, ctorTypes, interp)) {
1981 finalString << " instance.SetNew(&new_" << mappedname.c_str() << ");" << "\n";
1982 if (args.size()==0 && NeedDestructor(decl, interp))
1983 finalString << " instance.SetNewArray(&newArray_" << mappedname.c_str() << ");" << "\n";
1984 }
1985 if (NeedDestructor(decl, interp)) {
1986 finalString << " instance.SetDelete(&delete_" << mappedname.c_str() << ");" << "\n" << " instance.SetDeleteArray(&deleteArray_" << mappedname.c_str() << ");" << "\n" << " instance.SetDestructor(&destruct_" << mappedname.c_str() << ");" << "\n";
1987 }
1989 finalString << " instance.SetDirectoryAutoAdd(&directoryAutoAdd_" << mappedname.c_str() << ");" << "\n";
1990 }
1992 // We have a custom member function streamer or an older (not StreamerInfo based) automatic streamer.
1993 finalString << " instance.SetStreamerFunc(&streamer_" << mappedname.c_str() << ");" << "\n";
1994 }
1996 // We have a custom member function streamer or an older (not StreamerInfo based) automatic streamer.
1997 finalString << " instance.SetConvStreamerFunc(&conv_streamer_" << mappedname.c_str() << ");" << "\n";
1998 }
2000 finalString << " instance.SetMerge(&merge_" << mappedname.c_str() << ");" << "\n";
2001 }
2003 finalString << " instance.SetResetAfterMerge(&reset_" << mappedname.c_str() << ");" << "\n";
2004 }
2005 if (bset) {
2006 finalString << " instance.AdoptCollectionProxyInfo(TCollectionProxyInfo::Generate(TCollectionProxyInfo::" << "Pushback" << "<Internal::TStdBitsetHelper< " << classname.c_str() << " > >()));" << "\n";
2007
2008 needCollectionProxy = true;
2009 } else if (stl != 0 &&
2010 ((stl > 0 && stl<ROOT::kSTLend) || (stl < 0 && stl>-ROOT::kSTLend)) && // is an stl container
2011 (stl != ROOT::kSTLbitset && stl !=-ROOT::kSTLbitset) ){ // is no bitset
2012 int idx = classname.find("<");
2013 int stlType = (idx!=(int)std::string::npos) ? TClassEdit::STLKind(classname.substr(0,idx)) : 0;
2014 const char* methodTCP = nullptr;
2015 switch(stlType) {
2016 case ROOT::kSTLvector:
2017 case ROOT::kSTLlist:
2018 case ROOT::kSTLdeque:
2019 case ROOT::kROOTRVec:
2020 methodTCP="Pushback";
2021 break;
2023 methodTCP="Pushfront";
2024 break;
2025 case ROOT::kSTLmap:
2026 case ROOT::kSTLmultimap:
2029 methodTCP="MapInsert";
2030 break;
2031 case ROOT::kSTLset:
2032 case ROOT::kSTLmultiset:
2035 methodTCP="Insert";
2036 break;
2037 }
2038 // FIXME Workaround: for the moment we do not generate coll proxies with unique ptrs since
2039 // they imply copies and therefore do not compile.
2040 auto classNameForIO = TClassEdit::GetNameForIO(classname);
2041
2042 finalString << " static_assert(alignof(" << csymbol << "::value_type) <= 4096,\n";
2043 finalString << " \"Class with alignment strictly greater than 4096 are currently not supported in "
2044 "CollectionProxy. \"\n";
2045 finalString << " \"Please report this case to the developers\");\n";
2046 finalString << " instance.AdoptCollectionProxyInfo(TCollectionProxyInfo::Generate(TCollectionProxyInfo::" << methodTCP << "< " << classNameForIO.c_str() << " >()));" << "\n";
2047
2048 needCollectionProxy = true;
2049 }
2050
2051 //---------------------------------------------------------------------------
2052 // Register Alternate spelling of the class name.
2053 /////////////////////////////////////////////////////////////////////////////
2054
2055 if (cl.GetRequestedName()[0] && classname != cl.GetRequestedName()) {
2056 finalString << "\n" << " instance.AdoptAlternate(::ROOT::AddClassAlternate(\""
2057 << classname << "\",\"" << cl.GetRequestedName() << "\"));\n";
2058 }
2059
2060 if (!cl.GetDemangledTypeInfo().empty()
2061 && cl.GetDemangledTypeInfo() != classname
2062 && cl.GetDemangledTypeInfo() != cl.GetRequestedName()) {
2063 finalString << "\n" << " instance.AdoptAlternate(::ROOT::AddClassAlternate(\""
2064 << classname << "\",\"" << cl.GetDemangledTypeInfo() << "\"));\n";
2065
2066 }
2067
2068 //---------------------------------------------------------------------------
2069 // Register underlying SoA record for RNTuple SoA layouts
2070 /////////////////////////////////////////////////////////////////////////////
2071
2072 if (!cl.RequestedRNTupleSoARecord().empty()) {
2073 finalString << " instance.SetRNTupleSoARecord(\"" << cl.RequestedRNTupleSoARecord() << "\");" << "\n";
2074 }
2075
2076 //---------------------------------------------------------------------------
2077 // Pass the schema evolution rules to TGenericClassInfo
2078 /////////////////////////////////////////////////////////////////////////////
2079
2080 if( (rulesIt1 != ROOT::gReadRules.end() && rulesIt1->second.size()>0) || (rulesIt2 != ROOT::gReadRawRules.end() && rulesIt2->second.size()>0) ) {
2081 finalString << "\n" << " ::ROOT::Internal::TSchemaHelper* rule;" << "\n";
2082 }
2083
2084 if( rulesIt1 != ROOT::gReadRules.end() ) {
2085 finalString << "\n" << " // the io read rules" << "\n" << " std::vector<::ROOT::Internal::TSchemaHelper> readrules(" << rulesIt1->second.size() << ");" << "\n";
2086 ROOT::WriteSchemaList(rulesIt1->second.fRules, "readrules", finalString);
2087 finalString << " instance.SetReadRules( readrules );" << "\n";
2088 rulesIt1->second.fGenerated = true;
2089 }
2090
2091 if( rulesIt2 != ROOT::gReadRawRules.end() ) {
2092 finalString << "\n" << " // the io read raw rules" << "\n" << " std::vector<::ROOT::Internal::TSchemaHelper> readrawrules(" << rulesIt2->second.size() << ");" << "\n";
2093 ROOT::WriteSchemaList(rulesIt2->second.fRules, "readrawrules", finalString);
2094 finalString << " instance.SetReadRawRules( readrawrules );" << "\n";
2095 rulesIt2->second.fGenerated = true;
2096 }
2097
2098 finalString << " return &instance;" << "\n" << " }" << "\n";
2099
2101 // The GenerateInitInstance for STL are not unique and should not be externally accessible
2102 finalString << " TGenericClassInfo *GenerateInitInstance(const " << csymbol << "*)" << "\n" << " {\n return GenerateInitInstanceLocal(static_cast<" << csymbol << "*>(nullptr));\n }" << "\n";
2103 }
2104
2105 finalString << " // Static variable to force the class initialization" << "\n";
2106 // must be one long line otherwise UseDummy does not work
2107
2108
2109 finalString << " static ::ROOT::TGenericClassInfo *_R__UNIQUE_DICT_(Init) = GenerateInitInstanceLocal(static_cast<const " << csymbol << "*>(nullptr)); R__UseDummy(_R__UNIQUE_DICT_(Init));" << "\n";
2110
2111 if (!ClassInfo__HasMethod(decl,"Dictionary",interp) || IsTemplate(*decl)) {
2112 finalString << "\n" << " // Dictionary for non-ClassDef classes" << "\n"
2113 << " static TClass *" << mappedname << "_Dictionary() {\n"
2114 << " TClass* theClass ="
2115 << "::ROOT::GenerateInitInstanceLocal(static_cast<const " << csymbol << "*>(nullptr))->GetClass();\n"
2116 << " " << mappedname << "_TClassManip(theClass);\n";
2117 finalString << " return theClass;\n";
2118 finalString << " }\n\n";
2119
2120 // Now manipulate tclass in order to percolate the properties expressed as
2121 // annotations of the decls.
2122 std::string manipString;
2123 std::string attribute_s;
2124 std::string attrName, attrValue;
2125 // Class properties
2126 bool attrMapExtracted = false;
2127 if (decl->hasAttrs()){
2128 // Loop on the attributes
2129 for (clang::Decl::attr_iterator attrIt = decl->attr_begin();
2130 attrIt!=decl->attr_end();++attrIt){
2132 continue;
2133 }
2135 continue;
2136 }
2137 if (attrName == "name" ||
2138 attrName == "pattern" ||
2139 attrName == "rootmap") continue;
2140 // A general property
2141 // 1) We need to create the property map (in the gen code)
2142 // 2) we need to take out the map (in the gen code)
2143 // 3) We need to bookkep the fact that the map is created and out (in this source)
2144 // 4) We fill the map (in the gen code)
2145 if (!attrMapExtracted){
2146 manipString+=" theClass->CreateAttributeMap();\n";
2147 manipString+=" TDictAttributeMap* attrMap( theClass->GetAttributeMap() );\n";
2148 attrMapExtracted=true;
2149 }
2150 manipString+=" attrMap->AddProperty(\""+attrName +"\",\""+attrValue+"\");\n";
2151 }
2152 } // end of class has properties
2153
2154 // Member properties
2155 // Loop on declarations inside the class, including data members
2156 for(clang::CXXRecordDecl::decl_iterator internalDeclIt = decl->decls_begin();
2157 internalDeclIt != decl->decls_end(); ++internalDeclIt){
2158 if (!(!(*internalDeclIt)->isImplicit()
2159 && (clang::isa<clang::FieldDecl>(*internalDeclIt) ||
2160 clang::isa<clang::VarDecl>(*internalDeclIt)))) continue; // Check if it's a var or a field
2161
2162 // Now let's check the attributes of the var/field
2163 if (!internalDeclIt->hasAttrs()) continue;
2164
2165 attrMapExtracted = false;
2166 bool memberPtrCreated = false;
2167
2168 for (clang::Decl::attr_iterator attrIt = internalDeclIt->attr_begin();
2169 attrIt!=internalDeclIt->attr_end();++attrIt){
2170
2171 // Get the attribute as string
2173 continue;
2174 }
2175
2176 // Check the name of the decl
2177 clang::NamedDecl* namedInternalDecl = clang::dyn_cast<clang::NamedDecl> (*internalDeclIt);
2178 if (!namedInternalDecl) {
2179 TMetaUtils::Error(nullptr, "Cannot convert field declaration to clang::NamedDecl");
2180 continue;
2181 }
2182 const std::string memberName(namedInternalDecl->getName());
2183 const std::string cppMemberName = "theMember_"+memberName;
2184
2185 // Prepare a string to get the data member, it can be used later.
2186 const std::string dataMemberCreation= " TDataMember* "+cppMemberName+" = theClass->GetDataMember(\""+memberName+"\");\n";
2187
2188 // Let's now attack regular properties
2189
2191 continue;
2192 }
2193
2194 // Skip these
2195 if (attrName == propNames::comment ||
2196 attrName == propNames::iotype ||
2197 attrName == propNames::ioname ) continue;
2198
2199 if (!memberPtrCreated){
2201 memberPtrCreated=true;
2202 }
2203
2204 if (!attrMapExtracted){
2205 manipString+=" "+cppMemberName+"->CreateAttributeMap();\n";
2206 manipString+=" TDictAttributeMap* memberAttrMap_"+memberName+"( theMember_"+memberName+"->GetAttributeMap() );\n";
2207 attrMapExtracted=true;
2208 }
2209
2210 manipString+=" memberAttrMap_"+memberName+"->AddProperty(\""+attrName +"\",\""+attrValue+"\");\n";
2211
2212
2213 } // End loop on attributes
2214 } // End loop on internal declarations
2215
2216
2217 finalString << " static void " << mappedname << "_TClassManip(TClass* " << (manipString.empty() ? "":"theClass") << "){\n"
2218 << manipString
2219 << " }\n\n";
2220 } // End of !ClassInfo__HasMethod(decl,"Dictionary") || IsTemplate(*decl))
2221
2222 finalString << "} // end of namespace ROOT" << "\n" << "\n";
2223}
2224
2226 std::vector<std::string> &standaloneTargets,
2227 const cling::Interpreter &interp)
2228{
2230 if (!rulesIt1.second.fGenerated) {
2231 const clang::Type *typeptr = nullptr;
2232 const clang::CXXRecordDecl *target =
2233 ROOT::TMetaUtils::ScopeSearch(rulesIt1.first.c_str(), interp, true /*diag*/, &typeptr);
2234
2235 if (!target && !rulesIt1.second.fTargetDecl) {
2236 auto &&nRules = rulesIt1.second.size();
2237 std::string rule{nRules > 1 ? "rules" : "rule"};
2238 std::string verb{nRules > 1 ? "were" : "was"};
2239 ROOT::TMetaUtils::Warning(nullptr, "%d %s for target class %s %s not used!\n", nRules, rule.c_str(),
2240 rulesIt1.first.c_str(), verb.c_str());
2241 continue;
2242 }
2243
2246
2247 std::string name;
2249
2250 std::string mappedname;
2252
2253 finalString << "namespace ROOT {" << "\n";
2254 // Also TClingUtils.cxx:1823
2255 int i = 0;
2256 finalString << "\n // Schema evolution read functions\n";
2257 std::list<ROOT::SchemaRuleMap_t>::iterator rIt = rulesIt1.second.fRules.begin();
2258 while (rIt != rulesIt1.second.fRules.end()) {
2259
2260 //--------------------------------------------------------------------
2261 // Check if the rules refer to valid data members
2262 ///////////////////////////////////////////////////////////////////////
2263
2264 std::string error_string;
2266 ROOT::TMetaUtils::Warning(nullptr, "%s", error_string.c_str());
2267 rIt = rulesIt1.second.fRules.erase(rIt);
2268 continue;
2269 }
2270
2271 //---------------------------------------------------------------------
2272 // Write the conversion function if necessary
2273 ///////////////////////////////////////////////////////////////////////
2274
2275 if (rIt->find("code") != rIt->end()) {
2276 if (rawrules)
2278 else
2280 }
2281 ++rIt;
2282 }
2283 finalString << "} // namespace ROOT" << "\n";
2284
2285 standaloneTargets.push_back(rulesIt1.first);
2286 rulesIt1.second.fGenerated = true;
2287 }
2288 }
2289}
2290
2292 const std::vector<std::string> &standaloneTargets)
2293{
2294 std::string functionname("RecordReadRules_");
2296
2297 finalString << "namespace ROOT {" << "\n";
2298 finalString << " // Registration Schema evolution read functions\n";
2299 finalString << " int " << functionname << "() {" << "\n";
2300 if (!standaloneTargets.empty())
2301 finalString << "\n"
2302 << " ::ROOT::Internal::TSchemaHelper* rule;" << "\n";
2303 for (const auto &target : standaloneTargets) {
2304 std::string name;
2306
2307 ROOT::SchemaRuleClassMap_t::iterator rulesIt1 = ROOT::gReadRules.find(target.c_str());
2308 finalString << " {\n";
2309 if (rulesIt1 != ROOT::gReadRules.end()) {
2310 finalString << " // the io read rules for " << target << "\n";
2311 finalString << " std::vector<::ROOT::Internal::TSchemaHelper> readrules(" << rulesIt1->second.size()
2312 << ");" << "\n";
2313 ROOT::WriteSchemaList(rulesIt1->second.fRules, "readrules", finalString);
2314 finalString << " TClass::RegisterReadRules(TSchemaRule::kReadRule, \"" << name
2315 << "\", std::move(readrules));\n";
2316 rulesIt1->second.fGenerated = true;
2317 }
2318 ROOT::SchemaRuleClassMap_t::iterator rulesIt2 = ROOT::gReadRawRules.find(target.c_str());
2319 if (rulesIt2 != ROOT::gReadRawRules.end()) {
2320 finalString << "\n // the io read raw rules for " << target << "\n";
2321 finalString << " std::vector<::ROOT::Internal::TSchemaHelper> readrawrules(" << rulesIt2->second.size()
2322 << ");" << "\n";
2323 ROOT::WriteSchemaList(rulesIt2->second.fRules, "readrawrules", finalString);
2324 finalString << " TClass::RegisterReadRules(TSchemaRule::kReadRawRule, \"" << name
2325 << "\", std::move(readrawrules));\n";
2326 rulesIt2->second.fGenerated = true;
2327 }
2328 finalString << " }\n";
2329 }
2330 finalString << " return 0;\n";
2331 finalString << " }\n";
2332 finalString << " static int _R__UNIQUE_DICT_(ReadRules_" << dictName << ") = " << functionname << "();";
2333 finalString << "R__UseDummy(_R__UNIQUE_DICT_(ReadRules_" << dictName << "));" << "\n";
2334 finalString << "} // namespace ROOT" << "\n";
2335}
2336
2337////////////////////////////////////////////////////////////////////////////////
2338/// Return true if one of the class' enclosing scope is a namespace and
2339/// set fullname to the fully qualified name,
2340/// clsname to the name within a namespace
2341/// and nsname to the namespace fully qualified name.
2342
2344 std::string &clsname,
2345 std::string &nsname,
2346 const clang::CXXRecordDecl *cl)
2347{
2348 fullname.clear();
2349 nsname.clear();
2350
2352 clsname = fullname;
2353
2354 // Inline namespace are stripped from the normalized name, we need to
2355 // strip it from the prefix we want to remove.
2356 auto ctxt = cl->getEnclosingNamespaceContext();
2357 while(ctxt && ctxt!=cl && ctxt->isInlineNamespace()) {
2358 ctxt = ctxt->getParent();
2359 }
2360 if (ctxt) {
2361 const clang::NamedDecl *namedCtxt = llvm::dyn_cast<clang::NamedDecl>(ctxt);
2362 if (namedCtxt && namedCtxt!=cl) {
2363 const clang::NamespaceDecl *nsdecl = llvm::dyn_cast<clang::NamespaceDecl>(namedCtxt);
2364 if (nsdecl && !nsdecl->isAnonymousNamespace()) {
2366 clsname.erase (0, nsname.size() + 2);
2367 return true;
2368 }
2369 }
2370 }
2371 return false;
2372}
2373
2374////////////////////////////////////////////////////////////////////////////////
2375
2376const clang::DeclContext *GetEnclosingSpace(const clang::RecordDecl &cl)
2377{
2378 const clang::DeclContext *ctxt = cl.getDeclContext();
2379 while(ctxt && !ctxt->isNamespace()) {
2380 ctxt = ctxt->getParent();
2381 }
2382 return ctxt;
2383}
2384
2385////////////////////////////////////////////////////////////////////////////////
2386/// Write all the necessary opening part of the namespace and
2387/// return the number of closing brackets needed
2388/// For example for Space1::Space2
2389/// we write: namespace Space1 { namespace Space2 {
2390/// and return 2.
2391
2392int ROOT::TMetaUtils::WriteNamespaceHeader(std::ostream &out, const clang::DeclContext *ctxt)
2393{
2394 int closing_brackets = 0;
2395
2396 //fprintf(stderr,"DEBUG: in WriteNamespaceHeader for %s with %s\n",
2397 // cl.Fullname(),namespace_obj.Fullname());
2398 if (ctxt && ctxt->isNamespace()) {
2399 closing_brackets = WriteNamespaceHeader(out,ctxt->getParent());
2400 const clang::NamespaceDecl *ns = llvm::dyn_cast<clang::NamespaceDecl>(ctxt);
2401 if (ns) {
2402 for (int indent = 0; indent < closing_brackets; ++indent)
2403 out << " ";
2404 if (ns->isInline())
2405 out << "inline ";
2406 out << "namespace " << ns->getNameAsString() << " {" << std::endl;
2408 }
2409 }
2410
2411 return closing_brackets;
2412}
2413
2414////////////////////////////////////////////////////////////////////////////////
2415
2416int ROOT::TMetaUtils::WriteNamespaceHeader(std::ostream &out, const clang::RecordDecl *cl)
2417{
2418 return WriteNamespaceHeader(out, GetEnclosingSpace(*cl));
2419}
2420
2421////////////////////////////////////////////////////////////////////////////////
2422
2423bool ROOT::TMetaUtils::NeedTemplateKeyword(const clang::CXXRecordDecl *cl)
2424{
2425 clang::TemplateSpecializationKind kind = cl->getTemplateSpecializationKind();
2426 if (kind == clang::TSK_Undeclared ) {
2427 // Note a template;
2428 return false;
2429 } else if (kind == clang::TSK_ExplicitSpecialization) {
2430 // This is a specialized templated class
2431 return false;
2432 } else {
2433 // This is an automatically or explicitly instantiated templated class.
2434 return true;
2435 }
2436}
2437
2438////////////////////////////////////////////////////////////////////////////////
2439/// return true if we can find a custom operator new with placement
2440
2441bool ROOT::TMetaUtils::HasCustomOperatorNewPlacement(const char *which, const clang::RecordDecl &cl, const cling::Interpreter &interp)
2442{
2443 const char *name = which;
2444 const char *proto = "size_t";
2445 const char *protoPlacement = "size_t,void*";
2446
2447 // First search in the enclosing namespaces
2448 const clang::FunctionDecl *operatornew
2449 = ROOT::TMetaUtils::GetFuncWithProto(llvm::dyn_cast<clang::Decl>(cl.getDeclContext()),
2450 name, proto, interp,
2451 cling::LookupHelper::NoDiagnostics);
2452 const clang::FunctionDecl *operatornewPlacement
2453 = ROOT::TMetaUtils::GetFuncWithProto(llvm::dyn_cast<clang::Decl>(cl.getDeclContext()),
2455 cling::LookupHelper::NoDiagnostics);
2456
2457 const clang::DeclContext *ctxtnew = nullptr;
2458 const clang::DeclContext *ctxtnewPlacement = nullptr;
2459
2460 if (operatornew) {
2461 ctxtnew = operatornew->getParent();
2462 }
2465 }
2466
2467 // Then in the class and base classes
2469 false /*diags*/);
2472 false /*diags*/);
2473
2474 if (operatornew) {
2475 ctxtnew = operatornew->getParent();
2476 }
2479 }
2480
2481 if (!ctxtnewPlacement) {
2482 return false;
2483 }
2484 if (!ctxtnew) {
2485 // Only a new with placement, no hiding
2486 return true;
2487 }
2488 // Both are non zero
2489 if (ctxtnew == ctxtnewPlacement) {
2490 // Same declaration ctxt, no hiding
2491 return true;
2492 }
2493 const clang::CXXRecordDecl* clnew = llvm::dyn_cast<clang::CXXRecordDecl>(ctxtnew);
2494 const clang::CXXRecordDecl* clnewPlacement = llvm::dyn_cast<clang::CXXRecordDecl>(ctxtnewPlacement);
2495 if (!clnew && !clnewPlacement) {
2496 // They are both in different namespaces, I am not sure of the rules.
2497 // we probably ought to find which one is closest ... for now bail
2498 // (because rootcling was also bailing on that).
2499 return true;
2500 }
2501 if (clnew && !clnewPlacement) {
2502 // operator new is class method hiding the outer scope operator new with placement.
2503 return false;
2504 }
2505 if (!clnew && clnewPlacement) {
2506 // operator new is a not class method and can not hide new with placement which is a method
2507 return true;
2508 }
2509 // Both are class methods
2510 if (clnew->isDerivedFrom(clnewPlacement)) {
2511 // operator new is in a more derived part of the hierarchy, it is hiding operator new with placement.
2512 return false;
2513 }
2514 // operator new with placement is in a more derived part of the hierarchy, it can't be hidden by operator new.
2515 return true;
2516}
2517
2518////////////////////////////////////////////////////////////////////////////////
2519/// return true if we can find a custom operator new with placement
2520
2521bool ROOT::TMetaUtils::HasCustomOperatorNewPlacement(const clang::RecordDecl &cl, const cling::Interpreter &interp)
2522{
2523 return HasCustomOperatorNewPlacement("operator new",cl, interp);
2524}
2525
2526////////////////////////////////////////////////////////////////////////////////
2527/// return true if we can find a custom operator new with placement
2528
2529bool ROOT::TMetaUtils::HasCustomOperatorNewArrayPlacement(const clang::RecordDecl &cl, const cling::Interpreter &interp)
2530{
2531 return HasCustomOperatorNewPlacement("operator new[]",cl, interp);
2532}
2533
2534////////////////////////////////////////////////////////////////////////////////
2535/// std::string NormalizedName;
2536/// GetNormalizedName(NormalizedName, decl->getASTContext().getTypeDeclType(decl), interp, normCtxt);
2537
2539 const AnnotatedRecordDecl &cl,
2540 const clang::CXXRecordDecl *decl,
2541 const cling::Interpreter &interp,
2544{
2545 std::string classname = TClassEdit::GetLong64_Name(cl.GetNormalizedName());
2546
2547 std::string mappedname;
2548 ROOT::TMetaUtils::GetCppName(mappedname,classname.c_str());
2549
2550 // Write the functions that are need for the TGenericClassInfo.
2551 // This includes
2552 // IsA
2553 // operator new
2554 // operator new[]
2555 // operator delete
2556 // operator delete[]
2557
2558 ROOT::TMetaUtils::GetCppName(mappedname,classname.c_str());
2559
2560 if ( ! TClassEdit::IsStdClass( classname.c_str() ) ) {
2561
2562 // Prefix the full class name with '::' except for the STL
2563 // containers and std::string. This is to request the
2564 // real class instead of the class in the namespace ROOT::Shadow
2565 classname.insert(0,"::");
2566 }
2567
2568 finalString << "namespace ROOT {" << "\n";
2569
2570 std::string args;
2571 if (HasIOConstructor(decl, args, ctorTypes, interp)) {
2572 // write the constructor wrapper only for concrete classes
2573 finalString << " // Wrappers around operator new" << "\n";
2574 finalString << " static void *new_" << mappedname.c_str() << "(void *p) {" << "\n" << " return p ? ";
2576 finalString << "new(p) ";
2577 finalString << classname.c_str();
2578 finalString << args;
2579 finalString << " : ";
2580 } else {
2581 finalString << "::new(static_cast<::ROOT::Internal::TOperatorNewHelper*>(p)) ";
2582 finalString << classname.c_str();
2583 finalString << args;
2584 finalString << " : ";
2585 }
2586 finalString << "new " << classname.c_str() << args << ";" << "\n";
2587 finalString << " }" << "\n";
2588
2589 if (args.size()==0 && NeedDestructor(decl, interp)) {
2590 // Can not can newArray if the destructor is not public.
2591 finalString << " static void *newArray_";
2592 finalString << mappedname.c_str();
2593 finalString << "(Long_t nElements, void *p) {";
2594 finalString << "\n";
2595 finalString << " return p ? ";
2597 finalString << "new(p) ";
2598 finalString << classname.c_str();
2599 finalString << "[nElements] : ";
2600 } else {
2601 finalString << "::new(static_cast<::ROOT::Internal::TOperatorNewHelper*>(p)) ";
2602 finalString << classname.c_str();
2603 finalString << "[nElements] : ";
2604 }
2605 finalString << "new ";
2606 finalString << classname.c_str();
2607 finalString << "[nElements];";
2608 finalString << "\n";
2609 finalString << " }";
2610 finalString << "\n";
2611 }
2612 }
2613
2614 if (NeedDestructor(decl, interp)) {
2615 finalString << " // Wrapper around operator delete" << "\n" << " static void delete_" << mappedname.c_str() << "(void *p) {" << "\n" << " delete (static_cast<" << classname.c_str() << "*>(p));" << "\n" << " }" << "\n" << " static void deleteArray_" << mappedname.c_str() << "(void *p) {" << "\n" << " delete [] (static_cast<" << classname.c_str() << "*>(p));" << "\n" << " }" << "\n" << " static void destruct_" << mappedname.c_str() << "(void *p) {" << "\n" << " typedef " << classname.c_str() << " current_t;" << "\n" << " (static_cast<current_t*>(p))->~current_t();" << "\n" << " }" << "\n";
2616 }
2617
2619 finalString << " // Wrapper around the directory auto add." << "\n" << " static void directoryAutoAdd_" << mappedname.c_str() << "(void *p, TDirectory *dir) {" << "\n" << " ((" << classname.c_str() << "*)p)->DirectoryAutoAdd(dir);" << "\n" << " }" << "\n";
2620 }
2621
2623 finalString << " // Wrapper around a custom streamer member function." << "\n" << " static void streamer_" << mappedname.c_str() << "(TBuffer &buf, void *obj) {" << "\n" << " ((" << classname.c_str() << "*)obj)->" << classname.c_str() << "::Streamer(buf);" << "\n" << " }" << "\n";
2624 }
2625
2627 finalString << " // Wrapper around a custom streamer member function." << "\n" << " static void conv_streamer_" << mappedname.c_str() << "(TBuffer &buf, void *obj, const TClass *onfile_class) {" << "\n" << " ((" << classname.c_str() << "*)obj)->" << classname.c_str() << "::Streamer(buf,onfile_class);" << "\n" << " }" << "\n";
2628 }
2629
2630 if (HasNewMerge(decl, interp)) {
2631 finalString << " // Wrapper around the merge function." << "\n" << " static Long64_t merge_" << mappedname.c_str() << "(void *obj,TCollection *coll,TFileMergeInfo *info) {" << "\n" << " return ((" << classname.c_str() << "*)obj)->Merge(coll,info);" << "\n" << " }" << "\n";
2632 } else if (HasOldMerge(decl, interp)) {
2633 finalString << " // Wrapper around the merge function." << "\n" << " static Long64_t merge_" << mappedname.c_str() << "(void *obj,TCollection *coll,TFileMergeInfo *) {" << "\n" << " return ((" << classname.c_str() << "*)obj)->Merge(coll);" << "\n" << " }" << "\n";
2634 }
2635
2637 finalString << " // Wrapper around the Reset function." << "\n" << " static void reset_" << mappedname.c_str() << "(void *obj,TFileMergeInfo *info) {" << "\n" << " ((" << classname.c_str() << "*)obj)->ResetAfterMerge(info);" << "\n" << " }" << "\n";
2638 }
2639 finalString << "} // end of namespace ROOT for class " << classname.c_str() << "\n" << "\n";
2640}
2641
2642////////////////////////////////////////////////////////////////////////////////
2643/// Write interface function for STL members
2644
2646 const cling::Interpreter &interp,
2648{
2649 std::string a;
2650 std::string clName;
2651 TMetaUtils::GetCppName(clName, ROOT::TMetaUtils::GetFileName(*cl.GetRecordDecl(), interp).c_str());
2653 if (version == 0) return;
2654 if (version < 0 && !(cl.RequestStreamerInfo()) ) return;
2655
2656
2657 const clang::CXXRecordDecl *clxx = llvm::dyn_cast<clang::CXXRecordDecl>(cl.GetRecordDecl());
2658 if (!clxx) return;
2659
2660 // We also need to look at the base classes.
2661 for(clang::CXXRecordDecl::base_class_const_iterator iter = clxx->bases_begin(), end = clxx->bases_end();
2662 iter != end;
2663 ++iter)
2664 {
2665 int k = ROOT::TMetaUtils::IsSTLContainer(*iter);
2666 if (k!=0) {
2667 Internal::RStl::Instance().GenerateTClassFor( iter->getType(), interp, normCtxt);
2668 }
2669 }
2670
2671 // Loop over the non static data member.
2672 for(clang::RecordDecl::field_iterator field_iter = clxx->field_begin(), end = clxx->field_end();
2673 field_iter != end;
2674 ++field_iter)
2675 {
2676 std::string mTypename;
2678
2679 //member is a string
2680 {
2682 if (!strcmp(shortTypeName, "string")) {
2683 continue;
2684 }
2685 }
2686
2688
2690 if (k!=0) {
2691 // fprintf(stderr,"Add %s which is also",m.Type()->Name());
2692 // fprintf(stderr," %s\n",R__TrueName(**field_iter) );
2693 clang::QualType utype(ROOT::TMetaUtils::GetUnderlyingType(field_iter->getType()),0);
2694 Internal::RStl::Instance().GenerateTClassFor(utype, interp, normCtxt);
2695 }
2696 }
2697}
2698
2699////////////////////////////////////////////////////////////////////////////////
2700/// TrueName strips the typedefs and array dimensions.
2701
2702std::string ROOT::TMetaUtils::TrueName(const clang::FieldDecl &m)
2703{
2704 const clang::Type *rawtype = m.getType()->getCanonicalTypeInternal().getTypePtr();
2705 if (rawtype->isArrayType()) {
2706 rawtype = rawtype->getBaseElementTypeUnsafe ();
2707 }
2708
2709 std::string result;
2710 ROOT::TMetaUtils::GetQualifiedName(result, clang::QualType(rawtype,0), m);
2711 return result;
2712}
2713
2714////////////////////////////////////////////////////////////////////////////////
2715/// Return the version number of the class or -1
2716/// if the function Class_Version does not exist.
2717
2718int ROOT::TMetaUtils::GetClassVersion(const clang::RecordDecl *cl, const cling::Interpreter& interp)
2719{
2720 const clang::CXXRecordDecl* CRD = llvm::dyn_cast<clang::CXXRecordDecl>(cl);
2721 if (!CRD) {
2722 // Must be an enum or namespace.
2723 // FIXME: Make it work for a namespace!
2724 return -1;
2725 }
2726 const clang::FunctionDecl* funcCV = ROOT::TMetaUtils::ClassInfo__HasMethod(CRD,"Class_Version",interp);
2727
2728 // if we have no Class_Info() return -1.
2729 if (!funcCV) return -1;
2730
2731 // if we have many Class_Info() (?!) return 1.
2732 if (funcCV == (clang::FunctionDecl*)-1) return 1;
2733
2735}
2736
2737////////////////////////////////////////////////////////////////////////////////
2738/// If the function contains 'just': return SomeValue;
2739/// this routine will extract this value and return it.
2740/// The first element is set to true we have the body of the function and it
2741/// is indeed a trivial function with just a return of a value.
2742/// The second element contains the value (or -1 is case of failure)
2743
2744std::pair<bool, int>
2745ROOT::TMetaUtils::GetTrivialIntegralReturnValue(const clang::FunctionDecl *funcCV, const cling::Interpreter &interp)
2746{
2747 using res_t = std::pair<bool, int>;
2748
2749 const clang::CompoundStmt* FuncBody
2750 = llvm::dyn_cast_or_null<clang::CompoundStmt>(funcCV->getBody());
2751 if (!FuncBody)
2752 return res_t{false, -1};
2753 if (FuncBody->size() != 1) {
2754 // This is a non-ClassDef(), complex function - it might depend on state
2755 // and thus we'll need the runtime and cannot determine the result
2756 // statically.
2757 return res_t{false, -1};
2758 }
2759 const clang::ReturnStmt* RetStmt
2760 = llvm::dyn_cast<clang::ReturnStmt>(FuncBody->body_back());
2761 if (!RetStmt)
2762 return res_t{false, -1};
2763 const clang::Expr* RetExpr = RetStmt->getRetValue();
2764 // ClassDef controls the content of Class_Version() but not the return
2765 // expression which is CPP expanded from what the user provided as second
2766 // ClassDef argument. It's usually just be an integer literal but it could
2767 // also be an enum or a variable template for all we know.
2768 // Go through ICE to be more general.
2769 if (auto RetRes = RetExpr->getIntegerConstantExpr(funcCV->getASTContext())) {
2770 if (RetRes->isSigned())
2771 return res_t{true, (Version_t)RetRes->getSExtValue()};
2772 return res_t{true, (Version_t)RetRes->getZExtValue()};
2773 }
2774 return res_t{false, -1};
2775}
2776
2777////////////////////////////////////////////////////////////////////////////////
2778/// Is this an STL container.
2779
2781{
2782 return TMetaUtils::IsSTLCont(*annotated.GetRecordDecl());
2783}
2784
2785////////////////////////////////////////////////////////////////////////////////
2786/// Is this an STL container?
2787
2789{
2790 clang::QualType type = m.getType();
2792
2793 if (decl) return TMetaUtils::IsSTLCont(*decl);
2794 else return ROOT::kNotSTL;
2795}
2796
2797////////////////////////////////////////////////////////////////////////////////
2798/// Is this an STL container?
2799
2800int ROOT::TMetaUtils::IsSTLContainer(const clang::CXXBaseSpecifier &base)
2801{
2802 clang::QualType type = base.getType();
2804
2805 if (decl) return TMetaUtils::IsSTLCont(*decl);
2806 else return ROOT::kNotSTL;
2807}
2808
2809////////////////////////////////////////////////////////////////////////////////
2810/// Calls the given lambda on every header in the given module.
2811/// includeDirectlyUsedModules designates if the foreach should also loop over
2812/// the headers in all modules that are directly used via a `use` declaration
2813/// in the modulemap.
2815 const std::function<void(const clang::Module::Header &)> &closure,
2817{
2818 // Iterates over all headers in a module and calls the closure on each.
2819
2820 // Make a list of modules and submodules that we can check for headers.
2821 // We use a SetVector to prevent an infinite loop in unlikely case the
2822 // modules somehow are messed up and don't form a tree...
2823 llvm::SetVector<const clang::Module *> modules;
2824 modules.insert(&module);
2825 for (size_t i = 0; i < modules.size(); ++i) {
2826 const clang::Module *M = modules[i];
2827 for (const clang::Module *subModule : M->submodules())
2828 modules.insert(subModule);
2829 }
2830
2831 for (const clang::Module *m : modules) {
2833 for (clang::Module *used : m->DirectUses) {
2835 }
2836 }
2837
2838 // We want to check for all headers except the list of excluded headers here.
2839 for (auto HK : {clang::Module::HK_Normal, clang::Module::HK_Textual, clang::Module::HK_Private,
2840 clang::Module::HK_PrivateTextual}) {
2841 const auto &headerList = m->getHeaders(HK);
2842 for (const clang::Module::Header &moduleHeader : headerList) {
2844 }
2845 }
2846 }
2847}
2848
2849////////////////////////////////////////////////////////////////////////////////
2850/// Return the absolute type of typeDesc.
2851/// E.g.: typeDesc = "class TNamed**", returns "TNamed".
2852/// we remove * and const keywords. (we do not want to remove & ).
2853/// You need to use the result immediately before it is being overwritten.
2854
2856{
2857 static char t[4096];
2858 static const char* constwd = "const ";
2859 static const char* constwdend = "const";
2860
2861 const char *s;
2862 char *p=t;
2863 int lev=0;
2864 for (s=typeDesc;*s;s++) {
2865 if (*s=='<') lev++;
2866 if (*s=='>') lev--;
2867 if (lev==0 && *s=='*') continue;
2868 if (lev==0 && (strncmp(constwd,s,strlen(constwd))==0
2869 ||strcmp(constwdend,s)==0 ) ) {
2870 s+=strlen(constwd)-1; // -1 because the loop adds 1
2871 continue;
2872 }
2873 if (lev==0 && *s==' ' && *(s+1)!='*') { p = t; continue;}
2874 if (p - t > (long)sizeof(t)) {
2875 printf("ERROR (rootcling): type name too long for StortTypeName: %s\n",
2876 typeDesc);
2877 p[0] = 0;
2878 return t;
2879 }
2880 *p++ = *s;
2881 }
2882 p[0]=0;
2883
2884 return t;
2885}
2886
2887bool ROOT::TMetaUtils::IsStreamableObject(const clang::FieldDecl &m,
2888 const cling::Interpreter& interp)
2889{
2890 auto comment = ROOT::TMetaUtils::GetComment( m );
2891
2892 // Transient
2893 if (!comment.empty() && comment[0] == '!')
2894 return false;
2895
2896 clang::QualType type = m.getType();
2897
2898 if (type->isReferenceType()) {
2899 // Reference can not be streamed.
2900 return false;
2901 }
2902
2903 std::string mTypeName = type.getAsString(m.getASTContext().getPrintingPolicy());
2904 if (!strcmp(mTypeName.c_str(), "string") || !strcmp(mTypeName.c_str(), "string*")) {
2905 return true;
2906 }
2907 if (!strcmp(mTypeName.c_str(), "std::string") || !strcmp(mTypeName.c_str(), "std::string*")) {
2908 return true;
2909 }
2910
2912 return true;
2913 }
2914
2915 const clang::Type *rawtype = type.getTypePtr()->getBaseElementTypeUnsafe ();
2916
2917 if (rawtype->isPointerType()) {
2918 //Get to the 'raw' type.
2919 clang::QualType pointee;
2920 while ( (pointee = rawtype->getPointeeType()) , pointee.getTypePtrOrNull() && pointee.getTypePtr() != rawtype)
2921 {
2922 rawtype = pointee.getTypePtr();
2923 }
2924 }
2925
2926 if (rawtype->isFundamentalType() || rawtype->isEnumeralType()) {
2927 // not an ojbect.
2928 return false;
2929 }
2930
2931 const clang::CXXRecordDecl *cxxdecl = rawtype->getAsCXXRecordDecl();
2933 if (!(ROOT::TMetaUtils::ClassInfo__HasMethod(cxxdecl,"Class_Version", interp))) return true;
2935 if (version > 0) return true;
2936 }
2937 return false;
2938}
2939
2940////////////////////////////////////////////////////////////////////////////////
2941/// Return the absolute type of typeDesc.
2942/// E.g.: typeDesc = "class TNamed**", returns "TNamed".
2943/// we remove * and const keywords. (we do not want to remove & ).
2944/// You need to use the result immediately before it is being overwritten.
2945
2946std::string ROOT::TMetaUtils::ShortTypeName(const clang::FieldDecl &m)
2947{
2948 const clang::Type *rawtype = m.getType().getTypePtr();
2949
2950 //Get to the 'raw' type.
2951 clang::QualType pointee;
2952 while ( rawtype->isPointerType() && ((pointee = rawtype->getPointeeType()) , pointee.getTypePtrOrNull()) && pointee.getTypePtr() != rawtype)
2953 {
2954 rawtype = pointee.getTypePtr();
2955 }
2956
2957 std::string result;
2958 ROOT::TMetaUtils::GetQualifiedName(result, clang::QualType(rawtype,0), m);
2959 return result;
2960}
2961
2962////////////////////////////////////////////////////////////////////////////////
2963
2964clang::RecordDecl *ROOT::TMetaUtils::GetUnderlyingRecordDecl(clang::QualType type)
2965{
2966 const clang::Type *rawtype = ROOT::TMetaUtils::GetUnderlyingType(type);
2967
2968 if (rawtype->isFundamentalType() || rawtype->isEnumeralType()) {
2969 // not an object.
2970 return nullptr;
2971 }
2972 return rawtype->getAsCXXRecordDecl();
2973}
2974
2975////////////////////////////////////////////////////////////////////////////////
2976/// Generate the code of the class
2977/// If the requestor is genreflex, request the new streamer format
2978
2980 const AnnotatedRecordDecl &cl,
2981 const cling::Interpreter &interp,
2983 std::ostream& dictStream,
2985 bool isGenreflex=false)
2986{
2987 const clang::CXXRecordDecl* decl = llvm::dyn_cast<clang::CXXRecordDecl>(cl.GetRecordDecl());
2988
2989 if (!decl || !decl->isCompleteDefinition()) {
2990 return;
2991 }
2992
2993 std::string fullname;
2995 if (TClassEdit::IsSTLCont(fullname) ) {
2996 Internal::RStl::Instance().GenerateTClassFor(cl.GetNormalizedName(), llvm::dyn_cast<clang::CXXRecordDecl>(cl.GetRecordDecl()), interp, normCtxt);
2997 return;
2998 }
2999
3001 // The !genreflex is there to prevent genreflex to select collections which are data members
3002 // This is to maintain the behaviour of ROOT5 and ROOT6 up to 6.07 included.
3003 if (cl.RootFlag() && !isGenreflex) ROOT::TMetaUtils::WritePointersSTL(cl, interp, normCtxt); // In particular this detect if the class has a version number.
3004 if (!(cl.RequestNoStreamer())) {
3005 (*WriteStreamerFunc)(cl, interp, normCtxt, dictStream, isGenreflex || cl.RequestStreamerInfo());
3006 } else
3007 ROOT::TMetaUtils::Info(nullptr, "Class %s: Do not generate Streamer() [*** custom streamer ***]\n",fullname.c_str());
3008 } else {
3009 ROOT::TMetaUtils::Info(nullptr, "Class %s: Streamer() not declared\n", fullname.c_str());
3010
3011 // See comment above about the !isGenreflex
3013 }
3015}
3016
3017////////////////////////////////////////////////////////////////////////////////
3018/// Add any unspecified template parameters to the class template instance,
3019/// mentioned anywhere in the type.
3020///
3021/// Note: this does not strip any typedef but could be merged with cling::utils::Transform::GetPartiallyDesugaredType
3022/// if we can safely replace TClassEdit::IsStd with a test on the declaring scope
3023/// and if we can resolve the fact that the added parameter do not take into account possible use/dependences on Double32_t
3024/// and if we decide that adding the default is the right long term solution or not.
3025/// Whether it is or not depend on the I/O on whether the default template argument might change or not
3026/// and whether they (should) affect the on disk layout (for STL containers, we do know they do not).
3027
3029 const cling::Interpreter &interpreter,
3031{
3032 const clang::ASTContext& Ctx = interpreter.getCI()->getASTContext();
3033
3034 clang::QualType originalType = instanceType;
3035
3036 // In case of name* we need to strip the pointer first, add the default and attach
3037 // the pointer once again.
3038 if (llvm::isa<clang::PointerType>(instanceType.getTypePtr())) {
3039 // Get the qualifiers.
3040 clang::Qualifiers quals = instanceType.getQualifiers();
3041 clang::QualType newPointee = AddDefaultParameters(instanceType->getPointeeType(), interpreter, normCtxt);
3042 if (newPointee != instanceType->getPointeeType()) {
3043 instanceType = Ctx.getPointerType(newPointee);
3044 // Add back the qualifiers.
3045 instanceType = Ctx.getQualifiedType(instanceType, quals);
3046 }
3047 return instanceType;
3048 }
3049
3050 // In case of Int_t& we need to strip the pointer first, desugar and attach
3051 // the pointer once again.
3052 if (llvm::isa<clang::ReferenceType>(instanceType.getTypePtr())) {
3053 // Get the qualifiers.
3054 bool isLValueRefTy = llvm::isa<clang::LValueReferenceType>(instanceType.getTypePtr());
3055 clang::Qualifiers quals = instanceType.getQualifiers();
3056 clang::QualType newPointee = AddDefaultParameters(instanceType->getPointeeType(), interpreter, normCtxt);
3057
3058 if (newPointee != instanceType->getPointeeType()) {
3059 // Add the r- or l- value reference type back to the desugared one
3060 if (isLValueRefTy)
3061 instanceType = Ctx.getLValueReferenceType(newPointee);
3062 else
3063 instanceType = Ctx.getRValueReferenceType(newPointee);
3064 // Add back the qualifiers.
3065 instanceType = Ctx.getQualifiedType(instanceType, quals);
3066 }
3067 return instanceType;
3068 }
3069
3070 // Treat the Scope.
3071 bool prefix_changed = false;
3072 clang::NestedNameSpecifier prefix = std::nullopt;
3073 clang::Qualifiers prefix_qualifiers = instanceType.getLocalQualifiers();
3074 clang::NestedNameSpecifier desugaredPrefix = instanceType->getPrefix();
3075 if (desugaredPrefix) {
3076 // We have to also handle the prefix.
3078 prefix_changed = prefix != desugaredPrefix;
3079 // LLVM22: In the old API this was:
3080 // instanceType = clang::QualType(etype->getNamedType().getTypePtr(),0);
3081 }
3082
3083 // In case of template specializations iterate over the arguments and
3084 // add unspecified default parameter.
3085
3086 const clang::TemplateSpecializationType* TST
3087 = llvm::dyn_cast<const clang::TemplateSpecializationType>(instanceType.getTypePtr());
3088
3089 const clang::ClassTemplateSpecializationDecl* TSTdecl
3090 = llvm::dyn_cast_or_null<const clang::ClassTemplateSpecializationDecl>(instanceType.getTypePtr()->getAsCXXRecordDecl());
3091
3092 // Don't add the default paramater onto std classes.
3093 // We really need this for __shared_ptr which add a enum constant value which
3094 // is spelled in its 'numeral' form and thus the resulting type name is
3095 // incorrect. We also can used this for any of the STL collections where we
3096 // know we don't want the default argument. For the other members of the
3097 // std namespace this is dubious (because TMetaUtils::GetNormalizedName would
3098 // not drop those defaults). [I.e. the real test ought to be is std and
3099 // name is __shared_ptr or vector or list or set or etc.]
3101
3102 bool mightHaveChanged = false;
3103 if (TST && TSTdecl) {
3104
3105 clang::Sema& S = interpreter.getCI()->getSema();
3106 clang::TemplateDecl *Template = TSTdecl->getSpecializedTemplate()->getMostRecentDecl();
3107 clang::TemplateParameterList *Params = Template->getTemplateParameters();
3108 clang::TemplateParameterList::iterator Param = Params->begin(); // , ParamEnd = Params->end();
3109 //llvm::SmallVectorImpl<TemplateArgument> Converted; // Need to contains the other arguments.
3110 // Converted seems to be the same as our 'desArgs'
3111
3112 unsigned int dropDefault = normCtxt.GetConfig().DropDefaultArg(*Template);
3113
3114 llvm::SmallVector<clang::TemplateArgument, 4> desArgs;
3115 llvm::SmallVector<clang::TemplateArgument, 4> canonArgs;
3116 llvm::ArrayRef<clang::TemplateArgument> template_arguments = TST->template_arguments();
3117 unsigned int Idecl = 0, Edecl = TSTdecl->getTemplateArgs().size();
3118 // If we have more arguments than the TSTdecl, it is a variadic template
3119 // and we want all template arguments.
3120 if (template_arguments.size() > Edecl) {
3121 Edecl = template_arguments.size();
3122 }
3123 unsigned int maxAddArg = Edecl - dropDefault;
3124 for (const clang::TemplateArgument *I = template_arguments.begin(), *E = template_arguments.end(); Idecl != Edecl;
3125 I != E ? ++I : nullptr, ++Idecl, ++Param) {
3126
3127 if (I != E) {
3128
3129 if (I->getKind() == clang::TemplateArgument::Template) {
3130 clang::TemplateName templateName = I->getAsTemplate();
3131 clang::TemplateDecl* templateDecl = templateName.getAsTemplateDecl();
3132 if (templateDecl) {
3133 clang::DeclContext* declCtxt = templateDecl->getDeclContext();
3134
3135 if (declCtxt && !templateName.getAsQualifiedTemplateName()){
3136 clang::NamespaceDecl* ns = clang::dyn_cast<clang::NamespaceDecl>(declCtxt);
3137 clang::NestedNameSpecifier nns;
3138 if (ns) {
3139 nns = cling::utils::TypeName::CreateNestedNameSpecifier(Ctx, ns);
3140 } else if (clang::TagDecl* TD = llvm::dyn_cast<clang::TagDecl>(declCtxt)) {
3141 nns = cling::utils::TypeName::CreateNestedNameSpecifier(Ctx,TD, false /*FullyQualified*/);
3142 } else {
3143 // TU scope
3144 desArgs.push_back(*I);
3145 continue;
3146 }
3147 clang::TemplateName UnderlyingTN(templateDecl);
3148 if (clang::UsingShadowDecl *USD = templateName.getAsUsingShadowDecl())
3149 UnderlyingTN = clang::TemplateName(USD);
3150 clang::TemplateName templateNameWithNSS ( Ctx.getQualifiedTemplateName(nns, false, UnderlyingTN) );
3151 desArgs.push_back(clang::TemplateArgument(templateNameWithNSS));
3152 mightHaveChanged = true;
3153 continue;
3154 }
3155 }
3156 }
3157
3158 if (I->getKind() != clang::TemplateArgument::Type) {
3159 desArgs.push_back(*I);
3160 continue;
3161 }
3162
3163 clang::QualType SubTy = I->getAsType();
3164
3165 // Check if the type needs more desugaring and recurse.
3166 // (Originally this was limited to elaborated and templated type,
3167 // but we also need to do it for pointer and reference type
3168 // and who knows what, so do it always)
3169 clang::QualType newSubTy = AddDefaultParameters(SubTy,
3171 normCtxt);
3172 if (SubTy != newSubTy) {
3173 mightHaveChanged = true;
3174 desArgs.push_back(clang::TemplateArgument(newSubTy));
3175 } else {
3176 desArgs.push_back(*I);
3177 }
3178 // Converted.push_back(TemplateArgument(ArgTypeForTemplate));
3179 } else if (!isStdDropDefault && Idecl < maxAddArg) {
3180
3181 mightHaveChanged = true;
3182
3183 const clang::TemplateArgument& templateArg
3184 = TSTdecl->getTemplateArgs().get(Idecl);
3185 if (templateArg.getKind() != clang::TemplateArgument::Type) {
3186 desArgs.push_back(templateArg);
3187 continue;
3188 }
3189 clang::QualType SubTy = templateArg.getAsType();
3190
3191 clang::SourceLocation TemplateLoc = Template->getSourceRange ().getBegin(); //NOTE: not sure that this is the 'right' location.
3192 clang::SourceLocation RAngleLoc = TSTdecl->getSourceRange().getBegin(); // NOTE: most likely wrong, I think this is expecting the location of right angle
3193
3194 clang::TemplateTypeParmDecl *TTP = llvm::dyn_cast<clang::TemplateTypeParmDecl>(*Param);
3195 {
3196 // We may induce template instantiation
3197 cling::Interpreter::PushTransactionRAII clingRAII(const_cast<cling::Interpreter*>(&interpreter));
3198 bool HasDefaultArgs;
3199 clang::TemplateArgumentLoc ArgType = S.SubstDefaultTemplateArgumentIfAvailable(
3200 Template,
3201 /*TemplateKWLoc=*/clang::SourceLocation(),
3203 RAngleLoc,
3204 TTP,
3205 desArgs,
3206 canonArgs,
3208 // The substition can fail, in which case there would have been compilation
3209 // error printed on the screen.
3210 if (ArgType.getArgument().isNull()
3211 || ArgType.getArgument().getKind() != clang::TemplateArgument::Type) {
3212 ROOT::TMetaUtils::Error("ROOT::TMetaUtils::AddDefaultParameters",
3213 "Template parameter substitution failed for %s around %s\n",
3214 instanceType.getAsString().c_str(), SubTy.getAsString().c_str());
3215 break;
3216 }
3217 clang::QualType BetterSubTy = ArgType.getArgument().getAsType();
3218 SubTy = cling::utils::Transform::GetPartiallyDesugaredType(Ctx,BetterSubTy,normCtxt.GetConfig(),/*fullyQualified=*/ true);
3219 }
3221 desArgs.push_back(clang::TemplateArgument(SubTy));
3222 } else {
3223 // We are past the end of the list of specified arguements and we
3224 // do not want to add the default, no need to continue.
3225 break;
3226 }
3227 }
3228
3229 // If we added default parameter, allocate new type in the AST.
3230 if (mightHaveChanged) {
3231 instanceType = Ctx.getTemplateSpecializationType(TST->getKeyword(),
3232 TST->getTemplateName(),
3233 desArgs,
3234 /*CanonicalArgs=*/{},
3235 TST->getCanonicalTypeInternal());
3236 }
3237 }
3238
3240 if (prefix) {
3241 // LLVM22: In the old API this was:
3242 // instanceType = Ctx.getElaboratedType(clang::ElaboratedTypeKeyword::None, prefix, instanceType);
3243 instanceType = cling::utils::TypeName::QualifyTypeUnderPrefix(Ctx, instanceType, prefix);
3244 instanceType = Ctx.getQualifiedType(instanceType,prefix_qualifiers);
3245 }
3246 return instanceType;
3247}
3248
3249////////////////////////////////////////////////////////////////////////////////
3250/// ValidArrayIndex return a static string (so use it or copy it immediatly, do not
3251/// call GrabIndex twice in the same expression) containing the size of the
3252/// array data member.
3253/// In case of error, or if the size is not specified, GrabIndex returns 0.
3254/// If errnum is not null, *errnum updated with the error number:
3255/// Cint::G__DataMemberInfo::G__VALID : valid array index
3256/// Cint::G__DataMemberInfo::G__NOT_INT : array index is not an int
3257/// Cint::G__DataMemberInfo::G__NOT_DEF : index not defined before array
3258/// (this IS an error for streaming to disk)
3259/// Cint::G__DataMemberInfo::G__IS_PRIVATE: index exist in a parent class but is private
3260/// Cint::G__DataMemberInfo::G__UNKNOWN : index is not known
3261/// If errstr is not null, *errstr is updated with the address of a static
3262/// string containing the part of the index with is invalid.
3263
3264llvm::StringRef ROOT::TMetaUtils::DataMemberInfo__ValidArrayIndex(const cling::Interpreter &interp, const clang::DeclaratorDecl &m, int *errnum, llvm::StringRef *errstr)
3265{
3266 llvm::StringRef title;
3267
3268 // Try to get the comment either from the annotation or the header file if present
3269 if (clang::AnnotateAttr *A = m.getAttr<clang::AnnotateAttr>())
3270 title = A->getAnnotation();
3271 else
3272 // Try to get the comment from the header file if present
3274
3275 // Let's see if the user provided us with some information
3276 // with the format: //[dimension] this is the dim of the array
3277 // dimension can be an arithmetical expression containing, literal integer,
3278 // the operator *,+ and - and data member of integral type. In addition the
3279 // data members used for the size of the array need to be defined prior to
3280 // the array.
3281
3282 if (errnum) *errnum = VALID;
3283
3284 if (title.size() == 0 || (title[0] != '[')) return llvm::StringRef();
3285 size_t rightbracket = title.find(']');
3286 if (rightbracket == llvm::StringRef::npos) return llvm::StringRef();
3287
3288 std::string working;
3289 llvm::StringRef indexvar(title.data()+1,rightbracket-1);
3290
3291 // now we should have indexvar=dimension
3292 // Let's see if this is legal.
3293 // which means a combination of data member and digit separated by '*','+','-'
3294 // First we remove white spaces.
3295 unsigned int i;
3296 size_t indexvarlen = indexvar.size();
3297 for ( i=0; i<indexvarlen; i++) {
3298 if (!isspace(indexvar[i])) {
3299 working += indexvar[i];
3300 }
3301 }
3302
3303 // Now we go through all indentifiers
3304 const char *tokenlist = "*+-";
3305 char *current = const_cast<char*>(working.c_str());
3306 current = strtok(current,tokenlist); // this method does not need to be reentrant
3307
3308 while (current) {
3309 // Check the token
3310 if (isdigit(current[0])) {
3311 for(i=0;i<strlen(current);i++) {
3312 if (!isdigit(current[i])) {
3313 // Error we only access integer.
3314 //NOTE: *** Need to print an error;
3315 //fprintf(stderr,"*** Datamember %s::%s: size of array (%s) is not an interger\n",
3316 // member.MemberOf()->Name(), member.Name(), current);
3317 if (errstr) *errstr = current;
3318 if (errnum) *errnum = NOT_INT;
3319 return llvm::StringRef();
3320 }
3321 }
3322 } else { // current token is not a digit
3323 // first let's see if it is a data member:
3324 const clang::CXXRecordDecl *parent_clxx = llvm::dyn_cast<clang::CXXRecordDecl>(m.getDeclContext());
3325 const clang::FieldDecl *index1 = nullptr;
3326 if (parent_clxx)
3328 if ( index1 ) {
3329 if ( IsFieldDeclInt(index1) ) {
3330 // Let's see if it has already been written down in the
3331 // Streamer.
3332 // Let's see if we already wrote it down in the
3333 // streamer.
3334 for(clang::RecordDecl::field_iterator field_iter = parent_clxx->field_begin(), end = parent_clxx->field_end();
3335 field_iter != end;
3336 ++field_iter)
3337 {
3338 if ( field_iter->getNameAsString() == m.getNameAsString() ) {
3339 // we reached the current data member before
3340 // reaching the index so we have not written it yet!
3341 //NOTE: *** Need to print an error;
3342 //fprintf(stderr,"*** Datamember %s::%s: size of array (%s) has not been defined before the array \n",
3343 // member.MemberOf()->Name(), member.Name(), current);
3344 if (errstr) *errstr = current;
3345 if (errnum) *errnum = NOT_DEF;
3346 return llvm::StringRef();
3347 }
3348 if ( field_iter->getNameAsString() == index1->getNameAsString() ) {
3349 break;
3350 }
3351 } // end of while (m_local.Next())
3352 } else {
3353 //NOTE: *** Need to print an error;
3354 //fprintf(stderr,"*** Datamember %s::%s: size of array (%s) is not int \n",
3355 // member.MemberOf()->Name(), member.Name(), current);
3356 if (errstr) *errstr = current;
3357 if (errnum) *errnum = NOT_INT;
3358 return llvm::StringRef();
3359 }
3360 } else {
3361 // There is no variable by this name in this class, let see
3362 // the base classes!:
3363 int found = 0;
3364 if (parent_clxx) {
3365 clang::Sema& SemaR = const_cast<cling::Interpreter&>(interp).getSema();
3367 }
3368 if ( index1 ) {
3369 if ( IsFieldDeclInt(index1) ) {
3370 found = 1;
3371 } else {
3372 // We found a data member but it is the wrong type
3373 //NOTE: *** Need to print an error;
3374 //fprintf(stderr,"*** Datamember %s::%s: size of array (%s) is not int \n",
3375 // member.MemberOf()->Name(), member.Name(), current);
3376 if (errnum) *errnum = NOT_INT;
3377 if (errstr) *errstr = current;
3378 //NOTE: *** Need to print an error;
3379 //fprintf(stderr,"*** Datamember %s::%s: size of array (%s) is not int \n",
3380 // member.MemberOf()->Name(), member.Name(), current);
3381 if (errnum) *errnum = NOT_INT;
3382 if (errstr) *errstr = current;
3383 return llvm::StringRef();
3384 }
3385 if ( found && (index1->getAccess() == clang::AS_private) ) {
3386 //NOTE: *** Need to print an error;
3387 //fprintf(stderr,"*** Datamember %s::%s: size of array (%s) is a private member of %s \n",
3388 if (errstr) *errstr = current;
3389 if (errnum) *errnum = IS_PRIVATE;
3390 return llvm::StringRef();
3391 }
3392 }
3393 if (!found) {
3394 //NOTE: *** Need to print an error;
3395 //fprintf(stderr,"*** Datamember %s::%s: size of array (%s) is not known \n",
3396 // member.MemberOf()->Name(), member.Name(), indexvar);
3397 if (errstr) *errstr = indexvar;
3398 if (errnum) *errnum = UNKNOWN;
3399 return llvm::StringRef();
3400 } // end of if not found
3401 } // end of if is a data member of the class
3402 } // end of if isdigit
3403
3404 current = strtok(nullptr, tokenlist);
3405 } // end of while loop on tokens
3406
3407 return indexvar;
3408
3409}
3410
3411////////////////////////////////////////////////////////////////////////////////
3412/// Return (in the argument 'output') a valid name of the C++ symbol/type (pass as 'input')
3413/// that can be used in C++ as a variable name.
3414
3415void ROOT::TMetaUtils::GetCppName(std::string &out, const char *in)
3416{
3417 unsigned int i = 0;
3418 char c;
3419 out.clear();
3420 while((c = in[i++])) {
3421 const char *repl = nullptr;
3422 switch(c) {
3423 case '+': repl = "pL"; break;
3424 case '-': repl = "mI"; break;
3425 case '*': repl = "mU"; break;
3426 case '/': repl = "dI"; break;
3427 case '&': repl = "aN"; break;
3428 case '%': repl = "pE"; break;
3429 case '|': repl = "oR"; break;
3430 case '^': repl = "hA"; break;
3431 case '>': repl = "gR"; break;
3432 case '<': repl = "lE"; break;
3433 case '=': repl = "eQ"; break;
3434 case '~': repl = "wA"; break;
3435 case '.': repl = "dO"; break;
3436 case '(': repl = "oP"; break;
3437 case ')': repl = "cP"; break;
3438 case '[': repl = "oB"; break;
3439 case ']': repl = "cB"; break;
3440 case '{': repl = "lB"; break;
3441 case '}': repl = "rB"; break;
3442 case ';': repl = "sC"; break;
3443 case '#': repl = "hS"; break;
3444 case '?': repl = "qM"; break;
3445 case '`': repl = "bT"; break;
3446 case '!': repl = "nO"; break;
3447 case ',': repl = "cO"; break;
3448 case '$': repl = "dA"; break;
3449 case ' ': repl = "sP"; break;
3450 case ':': repl = "cL"; break;
3451 case '"': repl = "dQ"; break;
3452 case '@': repl = "aT"; break;
3453 case '\'': repl = "sQ"; break;
3454 case '\\': repl = "fI"; break;
3455 }
3456 if (repl)
3457 out.append(repl);
3458 else
3459 out.push_back(c);
3460 }
3461
3462 // If out is empty, or if it starts with a number, it's not a valid C++ variable. Prepend a "_"
3463 if (out.empty() || isdigit(out[0]))
3464 out.insert(out.begin(), '_');
3465}
3466
3467static clang::SourceLocation
3469 clang::SourceLocation sourceLoc) {
3470 // Follow macro expansion until we hit a source file.
3471 if (!sourceLoc.isFileID()) {
3472 return sourceManager.getExpansionRange(sourceLoc).getEnd();
3473 }
3474 return sourceLoc;
3475}
3476
3477////////////////////////////////////////////////////////////////////////////////
3478/// Return the header file to be included to declare the Decl.
3479
3480std::string ROOT::TMetaUtils::GetFileName(const clang::Decl& decl,
3481 const cling::Interpreter& interp)
3482{
3483 // It looks like the template specialization decl actually contains _less_ information
3484 // on the location of the code than the decl (in case where there is forward declaration,
3485 // that is what the specialization points to).
3486 //
3487 // const clang::CXXRecordDecl* clxx = llvm::dyn_cast<clang::CXXRecordDecl>(decl);
3488 // if (clxx) {
3489 // switch(clxx->getTemplateSpecializationKind()) {
3490 // case clang::TSK_Undeclared:
3491 // // We want the default behavior
3492 // break;
3493 // case clang::TSK_ExplicitInstantiationDeclaration:
3494 // case clang::TSK_ExplicitInstantiationDefinition:
3495 // case clang::TSK_ImplicitInstantiation: {
3496 // // We want the location of the template declaration:
3497 // const clang::ClassTemplateSpecializationDecl *tmplt_specialization = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl> (clxx);
3498 // if (tmplt_specialization) {
3499 // // return GetFileName(const_cast< clang::ClassTemplateSpecializationDecl *>(tmplt_specialization)->getSpecializedTemplate());
3500 // }
3501 // break;
3502 // }
3503 // case clang::TSK_ExplicitSpecialization:
3504 // // We want the default behavior
3505 // break;
3506 // default:
3507 // break;
3508 // }
3509 // }
3510
3511 using namespace clang;
3512 SourceLocation headerLoc = decl.getLocation();
3513
3514 static const char invalidFilename[] = "";
3515 if (!headerLoc.isValid()) return invalidFilename;
3516
3517 HeaderSearch& HdrSearch = interp.getCI()->getPreprocessor().getHeaderSearchInfo();
3518
3519 SourceManager& sourceManager = decl.getASTContext().getSourceManager();
3524 sourceManager.getIncludeLoc(headerFID));
3525
3526 OptionalFileEntryRef headerFE = sourceManager.getFileEntryRefForID(headerFID);
3527 while (includeLoc.isValid() && sourceManager.isInSystemHeader(includeLoc)) {
3529 // use HeaderSearch on the basename, to make sure it takes a header from
3530 // the include path (e.g. not from /usr/include/bits/)
3531 assert(headerFE && "Couldn't find FileEntry from FID!");
3532 auto FEhdr
3533 = HdrSearch.LookupFile(llvm::sys::path::filename(headerFE->getName()),
3535 true /*isAngled*/, nullptr/*FromDir*/, foundDir,
3536 ArrayRef<std::pair<OptionalFileEntryRef, DirectoryEntryRef>>(),
3537 nullptr/*Searchpath*/, nullptr/*RelPath*/,
3538 nullptr/*SuggestedModule*/, nullptr/*RequestingModule*/,
3539 nullptr/*IsMapped*/, nullptr /*IsFrameworkFound*/,
3540 false /*SkipCache*/,
3541 false /*BuildSystemModule*/,
3542 false /*OpenFile*/, true /*CacheFailures*/);
3543 if (FEhdr) break;
3544 headerFID = sourceManager.getFileID(includeLoc);
3545 headerFE = sourceManager.getFileEntryRefForID(headerFID);
3546 // If we have a system header in a module we can't just trace back the
3547 // original include with the preprocessor. But it should be enough if
3548 // we trace it back to the top-level system header that includes this
3549 // declaration.
3550 if (interp.getCI()->getLangOpts().Modules && !headerFE) {
3551 assert(decl.isFirstDecl() && "Couldn't trace back include from a decl"
3552 " that is not from an AST file");
3553 assert(StringRef(includeLoc.printToString(sourceManager)).starts_with("<module-includes>"));
3554 break;
3555 }
3557 sourceManager.getIncludeLoc(headerFID));
3558 }
3559
3560 if (!headerFE) return invalidFilename;
3561
3562 llvm::SmallString<256> headerFileName(headerFE->getName());
3563 // Remove double ../ from the path so that the search below finds a valid
3564 // longest match and does not result in growing paths.
3565 llvm::sys::path::remove_dots(headerFileName, /*remove_dot_dot=*/true);
3566
3567 // Now headerFID references the last valid system header or the original
3568 // user file.
3569 // Find out how to include it by matching file name to include paths.
3570 // We assume that the file "/A/B/C/D.h" can at some level be included as
3571 // "C/D.h". Be we cannot know whether that happens to be a different file
3572 // with the same name. Thus we first find the longest stem that can be
3573 // reached, say B/C/D.h. Then we find the shortest one, say C/D.h, that
3574 // points to the same file as the long version. If such a short version
3575 // exists it will be returned. If it doesn't the long version is returned.
3576 bool isAbsolute = llvm::sys::path::is_absolute(headerFileName);
3577 clang::OptionalFileEntryRef FELong;
3578 // Find the longest available match.
3579 for (llvm::sys::path::const_iterator
3580 IDir = llvm::sys::path::begin(headerFileName),
3581 EDir = llvm::sys::path::end(headerFileName);
3582 !FELong && IDir != EDir; ++IDir) {
3583 if (isAbsolute) {
3584 // skip "/" part
3585 isAbsolute = false;
3586 continue;
3587 }
3588 size_t lenTrailing = headerFileName.size() - (IDir->data() - headerFileName.data());
3589 llvm::StringRef trailingPart(IDir->data(), lenTrailing);
3590 assert(trailingPart.data() + trailingPart.size()
3591 == headerFileName.data() + headerFileName.size()
3592 && "Mismatched partitioning of file name!");
3595 true /*isAngled*/, nullptr/*FromDir*/, FoundDir,
3596 ArrayRef<std::pair<OptionalFileEntryRef, DirectoryEntryRef>>(),
3597 nullptr/*Searchpath*/, nullptr/*RelPath*/,
3598 nullptr/*SuggestedModule*/, nullptr/*RequestingModule*/,
3599 nullptr/*IsMapped*/, nullptr /*IsFrameworkFound*/);
3600 }
3601
3602 if (!FELong) {
3603 // We did not find any file part in any search path.
3604 return invalidFilename;
3605 }
3606
3607 // Iterates through path *parts* "C"; we need trailing parts "C/D.h"
3608 for (llvm::sys::path::reverse_iterator
3609 IDir = llvm::sys::path::rbegin(headerFileName),
3610 EDir = llvm::sys::path::rend(headerFileName);
3611 IDir != EDir; ++IDir) {
3612 size_t lenTrailing = headerFileName.size() - (IDir->data() - headerFileName.data());
3613 llvm::StringRef trailingPart(IDir->data(), lenTrailing);
3614 assert(trailingPart.data() + trailingPart.size()
3615 == headerFileName.data() + headerFileName.size()
3616 && "Mismatched partitioning of file name!");
3618 // Can we find it, and is it the same file as the long version?
3619 // (or are we back to the previously found spelling, which is fine, too)
3620 if (HdrSearch.LookupFile(trailingPart, SourceLocation(),
3621 true /*isAngled*/, nullptr/*FromDir*/, FoundDir,
3622 ArrayRef<std::pair<OptionalFileEntryRef, DirectoryEntryRef>>(),
3623 nullptr/*Searchpath*/, nullptr/*RelPath*/,
3624 nullptr/*SuggestedModule*/, nullptr/*RequestingModule*/,
3625 nullptr/*IsMapped*/, nullptr /*IsFrameworkFound*/) == FELong) {
3626 return trailingPart.str();
3627 }
3628 }
3629
3630 return invalidFilename;
3631}
3632
3633////////////////////////////////////////////////////////////////////////////////
3634
3636 const clang::QualType &qtype,
3637 const clang::ASTContext &astContext)
3638{
3639 std::string fqname = cling::utils::TypeName::GetFullyQualifiedName(qtype, astContext);
3643}
3644
3645////////////////////////////////////////////////////////////////////////////////
3646
3648 const clang::QualType &qtype,
3649 const cling::Interpreter &interpreter)
3650{
3651 // We need this because GetFullyQualifiedTypeName is triggering deserialization
3652 // This calling the same name function GetFullyQualifiedTypeName, but this should stay here because
3653 // callee doesn't have an interpreter pointer
3654 cling::Interpreter::PushTransactionRAII RAII(const_cast<cling::Interpreter*>(&interpreter));
3655
3657 qtype,
3658 interpreter.getCI()->getASTContext());
3659}
3660
3661////////////////////////////////////////////////////////////////////////////////
3662/// Get the template specialisation decl and template decl behind the qualtype
3663/// Returns true if successfully found, false otherwise
3664
3665bool ROOT::TMetaUtils::QualType2Template(const clang::QualType& qt,
3666 clang::ClassTemplateDecl*& ctd,
3667 clang::ClassTemplateSpecializationDecl*& ctsd)
3668{
3669 using namespace clang;
3670 const Type* theType = qt.getTypePtr();
3671 if (!theType){
3672 ctd=nullptr;
3673 ctsd=nullptr;
3674 return false;
3675 }
3676
3677 if (theType->isPointerType()) {
3678 return QualType2Template(theType->getPointeeType(), ctd, ctsd);
3679 }
3680
3681 if (const RecordType* rType = llvm::dyn_cast<RecordType>(theType)) {
3682 ctsd = llvm::dyn_cast_or_null<ClassTemplateSpecializationDecl>(rType->getDecl());
3683 if (ctsd) {
3684 ctd = ctsd->getSpecializedTemplate();
3685 return true;
3686 }
3687 }
3688
3689 if (const SubstTemplateTypeParmType* sttpType = llvm::dyn_cast<SubstTemplateTypeParmType>(theType)){
3690 return QualType2Template(sttpType->getReplacementType(), ctd, ctsd);
3691 }
3692
3693
3694 ctsd = llvm::dyn_cast_or_null<ClassTemplateSpecializationDecl>(qt->getAsCXXRecordDecl());
3695 if(ctsd){
3696 ctd = ctsd->getSpecializedTemplate();
3697 return true;
3698 }
3699
3700 ctd=nullptr;
3701 ctsd=nullptr;
3702 return false;
3703}
3704
3705////////////////////////////////////////////////////////////////////////////////
3706/// Extract from a qualtype the class template if this makes sense.
3707/// Retuns the ClassTemplateDecl or nullptr otherwise.
3708
3709clang::ClassTemplateDecl* ROOT::TMetaUtils::QualType2ClassTemplateDecl(const clang::QualType& qt)
3710{
3711 using namespace clang;
3715 return ctd;
3716}
3717
3718////////////////////////////////////////////////////////////////////////////////
3719/// These manipulations are necessary because a template specialisation type
3720/// does not inherit from a record type (there is an asymmetry between
3721/// the decls and the types in the clang interface).
3722/// We may need therefore to step into the "Decl dimension" to then get back
3723/// to the "type dimension".
3724
3725void ROOT::TMetaUtils::ExtractTemplateNameFromQualType(const clang::QualType& qt, clang::TemplateName& theTemplateName, clang::ElaboratedTypeKeyword& theKeyword)
3726{
3727 using namespace clang;
3728
3729 const Type* theType = qt.getTypePtr();
3730
3731 if (const TemplateSpecializationType* tst = llvm::dyn_cast_or_null<const TemplateSpecializationType>(theType)) {
3732 theTemplateName = tst->getTemplateName();
3733 theKeyword = tst->getKeyword();
3734 } // We step into the decl dimension
3737 }
3738}
3739
3740////////////////////////////////////////////////////////////////////////////////
3741
3742static bool areEqualTypes(const clang::TemplateArgument& tArg,
3743 llvm::SmallVectorImpl<clang::TemplateArgument>& preceedingTArgs,
3744 const clang::NamedDecl& tPar,
3745 const cling::Interpreter& interp,
3747{
3748 using namespace ROOT::TMetaUtils;
3749 using namespace clang;
3750
3751 // Check if this is a type for security
3752 TemplateTypeParmDecl* ttpdPtr = const_cast<TemplateTypeParmDecl*>(llvm::dyn_cast<TemplateTypeParmDecl>(&tPar));
3753 if (!ttpdPtr) return false;
3754 if (!ttpdPtr->hasDefaultArgument()) return false; // we should not be here in this case, but we protect us.
3755
3756 // Try the fast solution
3757 QualType tParQualType = ttpdPtr->getDefaultArgument().getArgument().getAsType();
3758 const QualType tArgQualType = tArg.getAsType();
3759
3760 // Now the equality tests for non template specialisations.
3761
3762 // The easy cases:
3763 // template <class T=double> class A; or
3764 // template <class T=A<float>> class B;
3765 if (tParQualType.getTypePtr() == tArgQualType.getTypePtr()) return true;
3766
3767 // Here the difficulty comes. We have to check if the argument is equal to its
3768 // default. We can do that bootstrapping an argument which has the default value
3769 // based on the preceeding arguments.
3770 // Basically we ask sema to give us the value of the argument given the template
3771 // of behind the parameter and the all the arguments.
3772 // So:
3773
3774 // Take the template out of the parameter
3775
3777 llvm::dyn_cast<TemplateSpecializationType>(tParQualType.getTypePtr());
3778
3779 if(!tst) // nothing more to be tried. They are different indeed.
3780 return false;
3781
3783 = llvm::dyn_cast_or_null<ClassTemplateSpecializationDecl>(tArgQualType->getAsCXXRecordDecl());
3784
3785 if(!TSTdecl) // nothing more to be tried. They are different indeed.
3786 return false;
3787
3788 TemplateDecl *Template = tst->getTemplateName().getAsTemplateDecl();
3789
3790 // Take the template location
3791 SourceLocation TemplateLoc = Template->getSourceRange ().getBegin();
3792
3793 // Get the position of the "<" (LA) of the specializaion
3794 SourceLocation LAngleLoc = TSTdecl->getSourceRange().getBegin();
3795
3796
3797 // Enclose in a scope for the RAII
3798 bool isEqual=false;
3800 {
3801 clang::Sema& S = interp.getCI()->getSema();
3802 cling::Interpreter::PushTransactionRAII clingRAII(const_cast<cling::Interpreter*>(&interp));
3803 llvm::SmallVector<clang::TemplateArgument, 4> canonArgs;
3804 bool HasDefaultArgs;
3805 TemplateArgumentLoc defTArgLoc = S.SubstDefaultTemplateArgumentIfAvailable(Template,
3806 /*TemplateKWLoc=*/clang::SourceLocation(),
3808 LAngleLoc,
3809 ttpdPtr,
3811 canonArgs,
3813 // The substition can fail, in which case there would have been compilation
3814 // error printed on the screen.
3815 newArg = defTArgLoc.getArgument();
3816 if (newArg.isNull() ||
3817 newArg.getKind() != clang::TemplateArgument::Type) {
3818 ROOT::TMetaUtils::Error("areEqualTypes",
3819 "Template parameter substitution failed!");
3820 }
3821
3823 = llvm::dyn_cast_or_null<ClassTemplateSpecializationDecl>(newArg.getAsType()->getAsCXXRecordDecl());
3824// std::cout << "nSTdecl is " << nTSTdecl << std::endl;
3825
3826 isEqual = (nTSTdecl && nTSTdecl->getMostRecentDecl() == TSTdecl->getMostRecentDecl()) ||
3827 (tParQualType.getTypePtr() == newArg.getAsType().getTypePtr());
3828 }
3829
3830
3831 return isEqual;
3832}
3833
3834
3835////////////////////////////////////////////////////////////////////////////////
3836/// std::cout << "Are equal values?\n";
3837
3838static bool areEqualValues(const clang::TemplateArgument& tArg,
3839 const clang::NamedDecl& tPar)
3840{
3841 using namespace clang;
3842 const NonTypeTemplateParmDecl* nttpdPtr = llvm::dyn_cast<NonTypeTemplateParmDecl>(&tPar);
3843 if (!nttpdPtr) return false;
3845
3846 if (!nttpd.hasDefaultArgument())
3847 return false;
3848
3849 // 64 bits wide and signed (non unsigned, that is why "false")
3850 llvm::APSInt defaultValueAPSInt(64, false);
3851 if (Expr* defArgExpr = nttpd.getDefaultArgument().getArgument().getAsExpr()) {
3852 const ASTContext& astCtxt = nttpdPtr->getASTContext();
3853 if (auto Value = defArgExpr->getIntegerConstantExpr(astCtxt))
3855 }
3856
3857 const int value = tArg.getAsIntegral().getLimitedValue();
3858
3859 // std::cout << (value == defaultValueAPSInt ? "yes!":"no") << std::endl;
3860 return value == defaultValueAPSInt;
3861}
3862
3863////////////////////////////////////////////////////////////////////////////////
3864/// Check if this NamedDecl is a template parameter with a default argument.
3865/// This is a single interface to treat both integral and type parameters.
3866/// Returns true if this is the case, false otherwise
3867
3868static bool isTypeWithDefault(const clang::NamedDecl* nDecl)
3869{
3870 using namespace clang;
3871 if (!nDecl) return false;
3872 if (const TemplateTypeParmDecl* ttpd = llvm::dyn_cast<TemplateTypeParmDecl>(nDecl))
3873 return ttpd->hasDefaultArgument();
3874 if (const NonTypeTemplateParmDecl* nttpd = llvm::dyn_cast<NonTypeTemplateParmDecl>(nDecl))
3875 return nttpd->hasDefaultArgument();
3876 return false;
3877
3878}
3879
3880static void KeepNParams(clang::QualType& normalizedType,
3881 const clang::QualType& vanillaType,
3882 const cling::Interpreter& interp,
3884
3885// Returns true if normTArg might have changed.
3886static bool RecurseKeepNParams(clang::TemplateArgument &normTArg,
3887 const clang::TemplateArgument &tArg,
3888 const cling::Interpreter& interp,
3890 const clang::ASTContext& astCtxt)
3891{
3892 using namespace ROOT::TMetaUtils;
3893 using namespace clang;
3894
3895 // Once we know there is no more default parameter, we can run through to the end
3896 // and/or recurse in the template parameter packs.
3897
3898 // If this is a type,
3899 // we need first of all to recurse: this argument may need to be manipulated
3900 if (tArg.getKind() == clang::TemplateArgument::Type) {
3901 QualType thisNormQualType = normTArg.getAsType();
3902 QualType thisArgQualType = tArg.getAsType();
3905 interp,
3906 normCtxt);
3909 } else if (normTArg.getKind() == clang::TemplateArgument::Pack) {
3910 assert( tArg.getKind() == clang::TemplateArgument::Pack );
3911
3913 bool mightHaveChanged = true;
3914 for (auto I = normTArg.pack_begin(), E = normTArg.pack_end(),
3915 FI = tArg.pack_begin(), FE = tArg.pack_end();
3916 I != E && FI != FE; ++I, ++FI)
3917 {
3920 desArgs.push_back(pack_arg);
3921 }
3922 if (mightHaveChanged) {
3923 ASTContext &mutableCtx( const_cast<ASTContext&>(astCtxt) );
3924 normTArg = TemplateArgument::CreatePackCopy(mutableCtx, desArgs);
3925 }
3926 return mightHaveChanged;
3927 }
3928 return false;
3929}
3930
3931
3932////////////////////////////////////////////////////////////////////////////////
3933/// This function allows to manipulate the number of arguments in the type
3934/// of a template specialisation.
3935
3936static void KeepNParams(clang::QualType& normalizedType,
3937 const clang::QualType& vanillaType,
3938 const cling::Interpreter& interp,
3940{
3941 using namespace ROOT::TMetaUtils;
3942 using namespace clang;
3943
3944 // If this type has no template specialisation behind, we don't need to do
3945 // anything
3948 if (! QualType2Template(vanillaType, ctd, ctsd)) return ;
3949
3950 // Even if this is a template, if we don't keep any argument, return
3951 const int nArgsToKeep = normCtxt.GetNargsToKeep(ctd);
3952
3953 // Important in case of early return: we must restore the original qualtype
3955
3956 const ASTContext& astCtxt = ctsd->getASTContext();
3957
3958
3959 // In case of name* we need to strip the pointer first, add the default and attach
3960 // the pointer once again.
3961 if (llvm::isa<clang::PointerType>(normalizedType.getTypePtr())) {
3962 // Get the qualifiers.
3963 clang::Qualifiers quals = normalizedType.getQualifiers();
3964 auto valNormalizedType = normalizedType->getPointeeType();
3966 normalizedType = astCtxt.getPointerType(valNormalizedType);
3967 // Add back the qualifiers.
3968 normalizedType = astCtxt.getQualifiedType(normalizedType, quals);
3969 return;
3970 }
3971
3972 // In case of Int_t& we need to strip the pointer first, desugar and attach
3973 // the pointer once again.
3974 if (llvm::isa<clang::ReferenceType>(normalizedType.getTypePtr())) {
3975 // Get the qualifiers.
3976 bool isLValueRefTy = llvm::isa<clang::LValueReferenceType>(normalizedType.getTypePtr());
3977 clang::Qualifiers quals = normalizedType.getQualifiers();
3978 auto valNormType = normalizedType->getPointeeType();
3980
3981 // Add the r- or l- value reference type back to the desugared one
3982 if (isLValueRefTy)
3983 normalizedType = astCtxt.getLValueReferenceType(valNormType);
3984 else
3985 normalizedType = astCtxt.getRValueReferenceType(valNormType);
3986 // Add back the qualifiers.
3987 normalizedType = astCtxt.getQualifiedType(normalizedType, quals);
3988 return;
3989 }
3990
3991 // Treat the Scope (factorise the code out to reuse it in AddDefaultParameters)
3992 bool prefix_changed = false;
3993 clang::NestedNameSpecifier prefix = std::nullopt;
3994 clang::Qualifiers prefix_qualifiers = normalizedType.getLocalQualifiers();
3996 if (desugaredPrefix) {
3997 // We have to also handle the prefix.
3998 // TODO: we ought to be running KeepNParams
4000 prefix_changed = prefix != desugaredPrefix;
4001 // LLVM22: In the old API this was:
4002 // normalizedType = clang::QualType(etype->getNamedType().getTypePtr(),0);
4003 }
4004
4005 // The canonical decl does not necessarily have the template default arguments.
4006 // Need to walk through the redecl chain to find it (we know there will be no
4007 // inconsistencies, at least)
4008 const clang::ClassTemplateDecl* ctdWithDefaultArgs = ctd;
4009 for (const RedeclarableTemplateDecl* rd: ctdWithDefaultArgs->redecls()) {
4010 clang::TemplateParameterList* tpl = rd->getTemplateParameters();
4011 if (tpl->getMinRequiredArguments () < tpl->size()) {
4012 ctdWithDefaultArgs = llvm::dyn_cast<clang::ClassTemplateDecl>(rd);
4013 break;
4014 }
4015 }
4016
4017 if (!ctdWithDefaultArgs) {
4018 Error("KeepNParams", "Not found template default arguments\n");
4020 return;
4021 }
4022
4023 TemplateParameterList* tParsPtr = ctdWithDefaultArgs->getTemplateParameters();
4025 const TemplateArgumentList& tArgs = ctsd->getTemplateArgs();
4026
4027 // We extract the template name from the type
4029 ElaboratedTypeKeyword theKeyword = ElaboratedTypeKeyword::None;
4030 ExtractTemplateNameFromQualType(normalizedType, theTemplateName, theKeyword);
4031 if (theTemplateName.isNull()) {
4033 return;
4034 }
4035
4037 llvm::dyn_cast<TemplateSpecializationType>(normalizedType.getTypePtr());
4038 if (!normalizedTst) {
4040 return;
4041 }
4042
4043 const clang::ClassTemplateSpecializationDecl* TSTdecl
4044 = llvm::dyn_cast_or_null<const clang::ClassTemplateSpecializationDecl>(normalizedType.getTypePtr()->getAsCXXRecordDecl());
4045 bool isStdDropDefault = TSTdecl && IsStdDropDefaultClass(*TSTdecl);
4046
4047 // Loop over the template parameters and arguments recursively.
4048 // We go down the two lanes: the one of template parameters (decls) and the
4049 // one of template arguments (QualTypes) in parallel. The former are a
4050 // property of the template, independent of its instantiations.
4051 // The latter are a property of the instance itself.
4052 llvm::SmallVector<TemplateArgument, 4> argsToKeep;
4053
4054 const int nArgs = tArgs.size();
4055 const auto &normArgs = normalizedTst->template_arguments();
4056 const int nNormArgs = normArgs.size();
4057
4058 bool mightHaveChanged = false;
4059 int latestNonDefaultArg = -1;
4060
4061 // becomes true when a parameter has a value equal to its default
4062 for (int formal = 0, inst = 0; formal != nArgs; ++formal, ++inst) {
4063 const NamedDecl* tParPtr = tPars.getParam(formal);
4064 if (!tParPtr) {
4065 Error("KeepNParams", "The parameter number %s is null.\n", formal);
4066 continue;
4067 }
4068
4069 // Stop if the normalized TemplateSpecializationType has less arguments than
4070 // the one index is pointing at.
4071 // We piggy back on the AddDefaultParameters routine basically.
4072 if (formal == nNormArgs || inst == nNormArgs) break;
4073
4074 const TemplateArgument& tArg = tArgs.get(formal);
4076
4077 bool shouldKeepArg = nArgsToKeep < 0 || inst < nArgsToKeep;
4078 if (isStdDropDefault) shouldKeepArg = false;
4079
4080 // Nothing to do here: either this parameter has no default, or we have to keep it.
4081 // FIXME: Temporary measure to get Atlas started with this.
4082 // We put a hard cut on the number of template arguments to keep, w/o checking if
4083 // they are non default. This makes this feature UNUSABLE for cases like std::vector,
4084 // where 2 different entities would have the same name if an allocator different from
4085 // the default one is by chance used.
4087 if ( tParPtr->isTemplateParameterPack() ) {
4088 // This is the last template parameter in the template declaration
4089 // but it is signaling that there can be an arbitrary number of arguments
4090 // in the template instance. So to avoid inadvertenly dropping those
4091 // arguments we just process all remaining argument and exit the main loop.
4092 for( ; inst != nNormArgs; ++inst) {
4095 argsToKeep.push_back(normTArg);
4096 }
4097 // Done.
4099 break;
4100 }
4102 argsToKeep.push_back(normTArg);
4104 continue;
4105 } else {
4106 if (!isStdDropDefault) {
4107 // Here we should not break but rather check if the value is the default one.
4108 mightHaveChanged = true;
4109 break;
4110 }
4111 // For std, we want to check the default args values.
4112 }
4113
4114 // Now, we keep it only if it not is equal to its default, expressed in the arg
4115 // Some gymnastic is needed to decide how to check for equality according to the
4116 // flavour of Type: templateType or Integer
4117 bool equal=false;
4118 auto argKind = tArg.getKind();
4119 if (argKind == clang::TemplateArgument::Type){
4120 // we need all the info
4122 } else if (argKind == clang::TemplateArgument::Integral){
4123 equal = areEqualValues(tArg, *tParPtr);
4124 }
4125
4126 argsToKeep.push_back(normTArg);
4127 if (!equal) {
4130 } else {
4131 mightHaveChanged = true;
4132 }
4133
4134
4135 } // of loop over parameters and arguments
4136
4137 if (latestNonDefaultArg >= 0)
4138 argsToKeep.resize(latestNonDefaultArg + 1);
4139
4142 return;
4143 }
4144
4145 // now, let's remanipulate our Qualtype
4146 if (mightHaveChanged) {
4147 Qualifiers qualifiers = normalizedType.getLocalQualifiers();
4148 normalizedType = astCtxt.getTemplateSpecializationType(theKeyword,
4150 argsToKeep,
4151 /*CanonicalArgs=*/{},
4153 normalizedType = astCtxt.getQualifiedType(normalizedType, qualifiers);
4154 }
4155
4156 // Here we have (prefix_changed==true || mightHaveChanged), in both case
4157 // we need to reconstruct the type.
4158 if (prefix) {
4159 // LLVM22: In the old API this was:
4160 // normalizedType = astCtxt.getElaboratedType(clang::ElaboratedTypeKeyword::None, prefix, normalizedType);
4161 normalizedType = cling::utils::TypeName::QualifyTypeUnderPrefix(astCtxt, normalizedType, prefix);
4163 }
4164}
4165
4166////////////////////////////////////////////////////////////////////////////////
4167/// Return the type normalized for ROOT,
4168/// keeping only the ROOT opaque typedef (Double32_t, etc.) and
4169/// adding default template argument for all types except those explicitly
4170/// requested to be drop by the user.
4171/// Default template for STL collections are not yet removed by this routine.
4172
4173clang::QualType ROOT::TMetaUtils::GetNormalizedType(const clang::QualType &type, const cling::Interpreter &interpreter, const TNormalizedCtxt &normCtxt)
4174{
4175 clang::ASTContext &ctxt = interpreter.getCI()->getASTContext();
4176
4177 // Modules can trigger deserialization.
4178 cling::Interpreter::PushTransactionRAII RAII(const_cast<cling::Interpreter*>(&interpreter));
4179 clang::QualType normalizedType = cling::utils::Transform::GetPartiallyDesugaredType(ctxt, type, normCtxt.GetConfig(), true /* fully qualify */);
4180
4181 // Readd missing default template parameters
4183
4184 // Get the number of arguments to keep in case they are not default.
4186
4187 return normalizedType;
4188}
4189
4190////////////////////////////////////////////////////////////////////////////////
4191/// Return the type name normalized for ROOT,
4192/// keeping only the ROOT opaque typedef (Double32_t, etc.) and
4193/// adding default template argument for all types except the STL collections
4194/// where we remove the default template argument if any.
4195///
4196/// This routine might actually belong in the interpreter because
4197/// cache the clang::Type might be intepreter specific.
4198
4199void ROOT::TMetaUtils::GetNormalizedName(std::string &norm_name, const clang::QualType &type, const cling::Interpreter &interpreter, const TNormalizedCtxt &normCtxt)
4200{
4201 if (type.isNull()) {
4202 norm_name = "";
4203 return;
4204 }
4205
4207
4208 clang::ASTContext &ctxt = interpreter.getCI()->getASTContext();
4209 clang::PrintingPolicy policy(ctxt.getPrintingPolicy());
4210 policy.SuppressTagKeyword = true; // Never get the class or struct keyword
4211 policy.SuppressTagKeywordInAnonNames = true; // Skip printing tags for anonymous entities
4212 policy.AnonymousTagLocations = false; // Do not extract file name + line number for anonymous types.
4213 // The scope suppression is required for getting rid of the anonymous part of the name of a class defined in an
4214 // anonymous namespace. In LLVM22 (and before), SuppressUnwrittenScope suppresses anonymous namespaces. Inline
4215 // namespace suppression is separately controlled by SuppressInlineNamespace, which we probably don't want to be
4216 // suppressed.
4217 policy.SuppressUnwrittenScope = true; // Strip anonymous namespace names
4218
4219 std::string normalizedNameStep1;
4220
4221 // getAsStringInternal can trigger deserialization
4222 cling::Interpreter::PushTransactionRAII clingRAII(const_cast<cling::Interpreter*>(&interpreter));
4223 normalizedType.getAsStringInternal(normalizedNameStep1,policy);
4224
4225 // Remove the _Atomic type specifyier if present before normalising
4228
4229 // Still remove the std:: and default template argument for STL container and
4230 // normalize the location and amount of white spaces.
4233
4237
4238 // The result of this routine is by definition a fully qualified name. There is an implicit starting '::' at the beginning of the name.
4239 // Depending on how the user typed their code, in particular typedef declarations, we may end up with an explicit '::' being
4240 // part of the result string. For consistency, we must remove it.
4241 if (norm_name.length()>2 && norm_name[0]==':' && norm_name[1]==':') {
4242 norm_name.erase(0,2);
4243 }
4244
4245}
4246
4247////////////////////////////////////////////////////////////////////////////////
4248
4250 const clang::TypeDecl* typeDecl,
4251 const cling::Interpreter &interpreter)
4252{
4254 const clang::Sema &sema = interpreter.getSema();
4255 clang::ASTContext& astCtxt = sema.getASTContext();
4256 clang::QualType qualType = astCtxt.getTypeDeclType(typeDecl);
4257
4259 qualType,
4261 tNormCtxt);
4262}
4263
4264////////////////////////////////////////////////////////////////////////////////
4265std::pair<std::string,clang::QualType>
4267 const cling::Interpreter &interpreter,
4270{
4271 std::string thisTypeName;
4272 GetNormalizedName(thisTypeName, thisType, interpreter, normCtxt );
4273 bool hasChanged;
4275 if (!hasChanged) return std::make_pair(thisTypeName,thisType);
4276
4278 ROOT::TMetaUtils::Info("ROOT::TMetaUtils::GetTypeForIO",
4279 "Name changed from %s to %s\n", thisTypeName.c_str(), thisTypeNameForIO.c_str());
4280 }
4281
4282 auto& lookupHelper = interpreter.getLookupHelper();
4283
4284 const clang::Type* typePtrForIO;
4286 cling::LookupHelper::DiagSetting::NoDiagnostics,
4287 &typePtrForIO);
4288
4289 // This should never happen
4290 if (!typePtrForIO) {
4291 ROOT::TMetaUtils::Fatal("ROOT::TMetaUtils::GetTypeForIO",
4292 "Type not found: %s.",thisTypeNameForIO.c_str());
4293 }
4294
4295 clang::QualType typeForIO(typePtrForIO,0);
4296
4297 // Check if this is a class. Indeed it could well be a POD
4298 if (!typeForIO->isRecordType()) {
4299 return std::make_pair(thisTypeNameForIO,typeForIO);
4300 }
4301
4302 auto thisDeclForIO = typeForIO->getAsCXXRecordDecl();
4303 if (!thisDeclForIO) {
4304 ROOT::TMetaUtils::Error("ROOT::TMetaUtils::GetTypeForIO",
4305 "The type for IO corresponding to %s is %s and it could not be found in the AST as class.\n", thisTypeName.c_str(), thisTypeNameForIO.c_str());
4306 return std::make_pair(thisTypeName,thisType);
4307 }
4308
4309 return std::make_pair(thisTypeNameForIO,typeForIO);
4310}
4311
4312////////////////////////////////////////////////////////////////////////////////
4313
4314clang::QualType ROOT::TMetaUtils::GetTypeForIO(const clang::QualType& thisType,
4315 const cling::Interpreter &interpreter,
4318{
4320}
4321
4322////////////////////////////////////////////////////////////////////////////////
4323/// Return the dictionary file name for a module
4324
4326{
4327 std::string dictFileName(moduleName);
4328 dictFileName += "_rdict.pcm";
4329 return dictFileName;
4330}
4331
4332int dumpDeclForAssert(const clang::Decl& D, const char* commentStart) {
4333 llvm::errs() << llvm::StringRef(commentStart, 80) << '\n';
4334 D.dump();
4335 return 0;
4336}
4337
4338////////////////////////////////////////////////////////////////////////////////
4339/// Returns the comment (// striped away), annotating declaration in a meaningful
4340/// for ROOT IO way.
4341/// Takes optional out parameter clang::SourceLocation returning the source
4342/// location of the comment.
4343///
4344/// CXXMethodDecls, FieldDecls and TagDecls are annotated.
4345/// CXXMethodDecls declarations and FieldDecls are annotated as follows:
4346/// Eg. void f(); // comment1
4347/// int member; // comment2
4348/// Inline definitions of CXXMethodDecls after the closing } \n. Eg:
4349/// void f()
4350/// {...} // comment3
4351/// TagDecls are annotated in the end of the ClassDef macro. Eg.
4352/// class MyClass {
4353/// ...
4354/// ClassDef(MyClass, 1) // comment4
4355///
4356
4357llvm::StringRef ROOT::TMetaUtils::GetComment(const clang::Decl &decl, clang::SourceLocation *loc)
4358{
4359 clang::SourceManager& sourceManager = decl.getASTContext().getSourceManager();
4360 clang::SourceLocation sourceLocation = decl.getEndLoc();
4361
4362 // If the location is a macro get the expansion location.
4363 sourceLocation = sourceManager.getExpansionRange(sourceLocation).getEnd();
4364 // FIXME: We should optimize this routine instead making it do the wrong thing
4365 // returning an empty comment if the decl came from the AST.
4366 // In order to do that we need to: check if the decl has an attribute and
4367 // return the attribute content (including walking the redecl chain) and if
4368 // this is not the case we should try finding it in the header file.
4369 // This will allow us to move the implementation of TCling*Info::Title() in
4370 // TClingDeclInfo.
4371 if (!decl.hasOwningModule() && sourceManager.isLoadedSourceLocation(sourceLocation)) {
4372 // Do not touch disk for nodes coming from the PCH.
4373 return "";
4374 }
4375
4376 bool invalid;
4377 const char *commentStart = sourceManager.getCharacterData(sourceLocation, &invalid);
4378 if (invalid)
4379 return "";
4380
4381 bool skipToSemi = true;
4382 if (const clang::FunctionDecl* FD = clang::dyn_cast<clang::FunctionDecl>(&decl)) {
4383 if (FD->isImplicit()) {
4384 // Compiler generated function.
4385 return "";
4386 }
4387 if (FD->isExplicitlyDefaulted() || FD->isDeletedAsWritten()) {
4388 // ctorOrFunc() = xyz; with commentStart pointing somewhere into
4389 // ctorOrFunc.
4390 // We have to skipToSemi
4391 } else if (FD->doesThisDeclarationHaveABody()) {
4392 // commentStart is at body's '}'
4393 // But we might end up e.g. at the ')' of a CPP macro
4394 assert((decl.getEndLoc() != sourceLocation || *commentStart == '}'
4396 && "Expected macro or end of body at '}'");
4397 if (*commentStart) ++commentStart;
4398
4399 // We might still have a ';'; skip the spaces and check.
4400 while (*commentStart && isspace(*commentStart)
4401 && *commentStart != '\n' && *commentStart != '\r') {
4402 ++commentStart;
4403 }
4404 if (*commentStart == ';') ++commentStart;
4405
4406 skipToSemi = false;
4407 }
4408 } else if (const clang::EnumConstantDecl* ECD
4409 = clang::dyn_cast<clang::EnumConstantDecl>(&decl)) {
4410 // either "konstant = 12, //COMMENT" or "lastkonstant // COMMENT"
4411 if (ECD->getNextDeclInContext())
4412 while (*commentStart && *commentStart != ',' && *commentStart != '\r' && *commentStart != '\n')
4413 ++commentStart;
4414 // else commentStart already points to the end.
4415
4416 skipToSemi = false;
4417 }
4418
4419 if (skipToSemi) {
4420 while (*commentStart && *commentStart != ';' && *commentStart != '\r' && *commentStart != '\n')
4421 ++commentStart;
4422 if (*commentStart == ';') ++commentStart;
4423 }
4424
4425 // Now skip the spaces until beginning of comments or EOL.
4426 while ( *commentStart && isspace(*commentStart)
4427 && *commentStart != '\n' && *commentStart != '\r') {
4428 ++commentStart;
4429 }
4430
4431 if (commentStart[0] != '/' ||
4432 (commentStart[1] != '/' && commentStart[1] != '*')) {
4433 // not a comment
4434 return "";
4435 }
4436
4437 // Treat by default c++ comments (+2) but also Doxygen comments (+4)
4438 // Int_t fPx; ///< Some doxygen comment for persistent data.
4439 // Int_t fPy; //!< Some doxygen comment for persistent data.
4440 // Int_t fPz; /*!< Some doxygen comment for persistent data. */
4441 // Int_t fPa; /**< Some doxygen comment for persistent data. */
4442 unsigned int skipChars = 2;
4443 if (commentStart[0] == '/' &&
4444 commentStart[1] == '/' &&
4445 (commentStart[2] == '/' || commentStart[2] == '!') &&
4446 commentStart[3] == '<') {
4447 skipChars = 4;
4448 } else if (commentStart[0] == '/' &&
4449 commentStart[1] == '*' &&
4450 (commentStart[2] == '*' || commentStart[2] == '!') &&
4451 commentStart[3] == '<') {
4452 skipChars = 4;
4453 }
4454
4456
4457 // Now skip the spaces after comment start until EOL.
4458 while ( *commentStart && isspace(*commentStart)
4459 && *commentStart != '\n' && *commentStart != '\r') {
4460 ++commentStart;
4461 }
4462 const char* commentEnd = commentStart;
4463 // Even for /* comments we only take the first line into account.
4464 while (*commentEnd && *commentEnd != '\n' && *commentEnd != '\r') {
4465 ++commentEnd;
4466 }
4467
4468 // "Skip" (don't include) trailing space.
4469 // *commentEnd points behind comment end thus check commentEnd[-1]
4470 while (commentEnd > commentStart && isspace(commentEnd[-1])) {
4471 --commentEnd;
4472 }
4473
4474 if (loc) {
4475 // Find the true beginning of a comment.
4476 unsigned offset = commentStart - sourceManager.getCharacterData(sourceLocation);
4477 *loc = sourceLocation.getLocWithOffset(offset - 1);
4478 }
4479
4480 return llvm::StringRef(commentStart, commentEnd - commentStart);
4481}
4482
4483////////////////////////////////////////////////////////////////////////////////
4484/// Return true if class has any of class declarations like ClassDef, ClassDefNV, ClassDefOverride
4485
4486bool ROOT::TMetaUtils::HasClassDefMacro(const clang::Decl *decl, const cling::Interpreter &interpreter)
4487{
4488 if (!decl) return false;
4489
4490 auto& sema = interpreter.getCI()->getSema();
4491 auto maybeMacroLoc = decl->getLocation();
4492
4493 if (!maybeMacroLoc.isMacroID()) return false;
4494
4495 static const std::vector<std::string> signatures =
4496 { "ClassDef", "ClassDefOverride", "ClassDefNV", "ClassDefInline", "ClassDefInlineOverride", "ClassDefInlineNV" };
4497
4498 for (auto &name : signatures)
4499 if (sema.findMacroSpelling(maybeMacroLoc, name))
4500 return true;
4501
4502 return false;
4503}
4504
4505////////////////////////////////////////////////////////////////////////////////
4506/// Return the class comment after the ClassDef:
4507/// class MyClass {
4508/// ...
4509/// ClassDef(MyClass, 1) // class comment
4510///
4511
4512llvm::StringRef ROOT::TMetaUtils::GetClassComment(const clang::CXXRecordDecl &decl,
4513 clang::SourceLocation *loc,
4514 const cling::Interpreter &interpreter)
4515{
4516 using namespace clang;
4517
4518 const Decl* DeclFileLineDecl
4519 = interpreter.getLookupHelper().findFunctionProto(&decl, "DeclFileLine", "",
4520 cling::LookupHelper::NoDiagnostics);
4521
4522 // For now we allow only a special macro (ClassDef) to have meaningful comments
4525 llvm::StringRef comment = ROOT::TMetaUtils::GetComment(*DeclFileLineDecl, &commentSLoc);
4526 if (comment.size()) {
4527 if (loc) {
4528 *loc = commentSLoc;
4529 }
4530 return comment;
4531 }
4532 }
4533 return llvm::StringRef();
4534}
4535
4536////////////////////////////////////////////////////////////////////////////////
4537/// Return the base/underlying type of a chain of array or pointers type.
4538/// Does not yet support the array and pointer part being intermixed.
4539
4540const clang::Type *ROOT::TMetaUtils::GetUnderlyingType(clang::QualType type)
4541{
4542 const clang::Type *rawtype = type.getTypePtr();
4543
4544 // NOTE: We probably meant isa<clang::ElaboratedType>
4545 if (rawtype->isElaboratedTypeSpecifier() ) {
4546 rawtype = rawtype->getCanonicalTypeInternal().getTypePtr();
4547 }
4548 if (rawtype->isArrayType()) {
4549 rawtype = type.getTypePtr()->getBaseElementTypeUnsafe ();
4550 }
4551 if (rawtype->isPointerType() || rawtype->isReferenceType() ) {
4552 //Get to the 'raw' type.
4553 clang::QualType pointee;
4554 while ( (pointee = rawtype->getPointeeType()) , pointee.getTypePtrOrNull() && pointee.getTypePtr() != rawtype)
4555 {
4556 rawtype = pointee.getTypePtr();
4557
4558 if (rawtype->isElaboratedTypeSpecifier() ) {
4559 rawtype = rawtype->getCanonicalTypeInternal().getTypePtr();
4560 }
4561 if (rawtype->isArrayType()) {
4562 rawtype = rawtype->getBaseElementTypeUnsafe ();
4563 }
4564 }
4565 }
4566 if (rawtype->isArrayType()) {
4567 rawtype = rawtype->getBaseElementTypeUnsafe ();
4568 }
4569 return rawtype;
4570}
4571
4572////////////////////////////////////////////////////////////////////////////////
4573/// Return true if the DeclContext is representing an entity reacheable from the
4574/// global namespace
4575
4576bool ROOT::TMetaUtils::IsCtxtReacheable(const clang::DeclContext &ctxt)
4577{
4578 if (ctxt.isNamespace() || ctxt.isTranslationUnit())
4579 return true;
4580 else if(const auto parentdecl = llvm::dyn_cast<clang::CXXRecordDecl>(&ctxt))
4582 else
4583 // For example "extern C" context.
4584 return true;
4585}
4586
4587////////////////////////////////////////////////////////////////////////////////
4588/// Return true if the decl is representing an entity reacheable from the
4589/// global namespace
4590
4592{
4593 const clang::DeclContext *ctxt = decl.getDeclContext();
4594 switch (decl.getAccess()) {
4595 case clang::AS_public:
4596 return !ctxt || IsCtxtReacheable(*ctxt);
4597 case clang::AS_protected:
4598 return false;
4599 case clang::AS_private:
4600 return false;
4601 case clang::AS_none:
4602 return !ctxt || IsCtxtReacheable(*ctxt);
4603 default:
4604 // IMPOSSIBLE
4605 assert(false && "Unexpected value for the access property value in Clang");
4606 return false;
4607 }
4608}
4609
4610////////////////////////////////////////////////////////////////////////////////
4611/// Return true, if the decl is part of the std namespace.
4612
4613bool ROOT::TMetaUtils::IsStdClass(const clang::RecordDecl &cl)
4614{
4615 return cling::utils::Analyze::IsStdClass(cl);
4616}
4617
4618////////////////////////////////////////////////////////////////////////////////
4619/// Return true, if the decl is part of the std namespace and we want
4620/// its default parameter dropped.
4621
4622bool ROOT::TMetaUtils::IsStdDropDefaultClass(const clang::RecordDecl &cl)
4623{
4624 // Might need to reduce it to shared_ptr and STL collection.s
4625 if (cling::utils::Analyze::IsStdClass(cl)) {
4626 static const char *names[] =
4627 { "shared_ptr", "__shared_ptr",
4628 "vector", "list", "deque", "map", "multimap", "set", "multiset", "bitset"};
4629 llvm::StringRef clname(cl.getName());
4630 for(auto &&name : names) {
4631 if (clname == name) return true;
4632 }
4633 }
4634 return false;
4635}
4636
4637////////////////////////////////////////////////////////////////////////////////
4638/// This is a recursive function
4639
4640bool ROOT::TMetaUtils::MatchWithDeclOrAnyOfPrevious(const clang::CXXRecordDecl &cl,
4641 const clang::CXXRecordDecl &currentCl)
4642{
4643 // We found it: let's return true
4644 if (&cl == &currentCl) return true;
4645
4646 const clang::CXXRecordDecl* previous = currentCl.getPreviousDecl();
4647
4648 // There is no previous decl, so we cannot possibly find it
4649 if (nullptr == previous){
4650 return false;
4651 }
4652
4653 // We try to find it in the previous
4655
4656}
4657
4658//______________________________________________________________________________
4659
4660bool ROOT::TMetaUtils::IsOfType(const clang::CXXRecordDecl &cl, const std::string& typ, const cling::LookupHelper& lh)
4661{
4662 // Return true if the decl is of type.
4663 // A proper hashtable for caching results would be the ideal solution
4664 // 1) Only one lookup per type
4665 // 2) No string comparison
4666 // We may use a map which becomes an unordered map if c++11 is enabled?
4667
4668 const clang::CXXRecordDecl *thisDecl =
4669 llvm::dyn_cast_or_null<clang::CXXRecordDecl>(lh.findScope(typ, cling::LookupHelper::WithDiagnostics));
4670
4671 // this would be probably an assert given that this state is not reachable unless a mistake is somewhere
4672 if (! thisDecl){
4673 Error("IsOfType","Record decl of type %s not found in the AST.", typ.c_str());
4674 return false;
4675 }
4676
4677 // Now loop on all previous decls to seek a match
4678 const clang::CXXRecordDecl *mostRecentDecl = thisDecl->getMostRecentDecl();
4680
4681 return matchFound;
4682}
4683
4684////////////////////////////////////////////////////////////////////////////////
4685/// type : type name: vector<list<classA,allocator>,allocator>
4686/// result: 0 : not stl container
4687/// abs(result): code of container 1=vector,2=list,3=deque,4=map
4688/// 5=multimap,6=set,7=multiset
4689
4691{
4692 // This routine could be enhanced to also support:
4693 //
4694 // testAlloc: if true, we test allocator, if it is not default result is negative
4695 // result: 0 : not stl container
4696 // abs(result): code of container 1=vector,2=list,3=deque,4=map
4697 // 5=multimap,6=set,7=multiset
4698 // positive val: we have a vector or list with default allocator to any depth
4699 // like vector<list<vector<int>>>
4700 // negative val: STL container other than vector or list, or non default allocator
4701 // For example: vector<deque<int>> has answer -1
4702
4703 if (!IsStdClass(cl)) {
4704 auto *nsDecl = llvm::dyn_cast<clang::NamespaceDecl>(cl.getDeclContext());
4705 if (cl.getName() != "RVec" || nsDecl == nullptr || nsDecl->getName() != "VecOps")
4706 return ROOT::kNotSTL;
4707
4708 auto *parentNsDecl = llvm::dyn_cast<clang::NamespaceDecl>(cl.getDeclContext()->getParent());
4709 if (parentNsDecl == nullptr || parentNsDecl->getName() != "ROOT")
4710 return ROOT::kNotSTL;
4711 }
4712
4713 return STLKind(cl.getName());
4714}
4715
4716static bool hasSomeTypedefSomewhere(const clang::Type* T) {
4717 using namespace clang;
4718 struct SearchTypedef: public TypeVisitor<SearchTypedef, bool> {
4719 bool VisitTypedefType(const TypedefType* TD) {
4720 return true;
4721 }
4722 bool VisitArrayType(const ArrayType* AT) {
4723 return Visit(AT->getElementType().getTypePtr());
4724 }
4725 bool VisitDecltypeType(const DecltypeType* DT) {
4726 return Visit(DT->getUnderlyingType().getTypePtr());
4727 }
4728 bool VisitPointerType(const PointerType* PT) {
4729 return Visit(PT->getPointeeType().getTypePtr());
4730 }
4731 bool VisitReferenceType(const ReferenceType* RT) {
4732 return Visit(RT->getPointeeType().getTypePtr());
4733 }
4735 return Visit(STST->getReplacementType().getTypePtr());
4736 }
4738 for (const TemplateArgument &TA : TST->template_arguments()) {
4739 if (TA.getKind() == TemplateArgument::Type && Visit(TA.getAsType().getTypePtr()))
4740 return true;
4741 }
4742 return false;
4743 }
4745 return false; // shrug...
4746 }
4747 bool VisitTypeOfType(const TypeOfType* TOT) {
4748 return TOT->getUnmodifiedType().getTypePtr();
4749 }
4750 };
4751
4753 return ST.Visit(T);
4754}
4755
4756////////////////////////////////////////////////////////////////////////////////
4757/// Check if 'input' or any of its template parameter was substituted when
4758/// instantiating the class template instance and replace it with the
4759/// partially sugared types we have from 'instance'.
4760
4761clang::QualType ROOT::TMetaUtils::ReSubstTemplateArg(clang::QualType input, const clang::Type *instance)
4762{
4763 if (!instance) return input;
4764 // if there is no typedef in instance then there is nothing guiding any
4765 // template parameter typedef replacement.
4767 return input;
4768
4769 using namespace llvm;
4770 using namespace clang;
4771 const clang::ASTContext &Ctxt = instance->getAsCXXRecordDecl()->getASTContext();
4772
4773 // LLVM22: No elaborated type anymore
4774
4775 QualType QT = input;
4776
4777 // In case of Int_t* we need to strip the pointer first, ReSubst and attach
4778 // the pointer once again.
4779 if (isa<clang::PointerType>(QT.getTypePtr())) {
4780 // Get the qualifiers.
4781 Qualifiers quals = QT.getQualifiers();
4782 QualType nQT;
4783 nQT = ReSubstTemplateArg(QT->getPointeeType(),instance);
4784 if (nQT == QT->getPointeeType()) return QT;
4785
4786 QT = Ctxt.getPointerType(nQT);
4787 // Add back the qualifiers.
4788 QT = Ctxt.getQualifiedType(QT, quals);
4789 return QT;
4790 }
4791
4792 // In case of Int_t& we need to strip the pointer first, ReSubst and attach
4793 // the reference once again.
4794 if (isa<ReferenceType>(QT.getTypePtr())) {
4795 // Get the qualifiers.
4796 bool isLValueRefTy = isa<LValueReferenceType>(QT.getTypePtr());
4797 Qualifiers quals = QT.getQualifiers();
4798 QualType nQT;
4799 nQT = ReSubstTemplateArg(QT->getPointeeType(),instance);
4800 if (nQT == QT->getPointeeType()) return QT;
4801
4802 // Add the r- or l-value reference type back to the desugared one.
4803 if (isLValueRefTy)
4804 QT = Ctxt.getLValueReferenceType(nQT);
4805 else
4806 QT = Ctxt.getRValueReferenceType(nQT);
4807 // Add back the qualifiers.
4808 QT = Ctxt.getQualifiedType(QT, quals);
4809 return QT;
4810 }
4811
4812 // In case of Int_t[2] we need to strip the array first, ReSubst and attach
4813 // the array once again.
4814 if (isa<clang::ArrayType>(QT.getTypePtr())) {
4815 // Get the qualifiers.
4816 Qualifiers quals = QT.getQualifiers();
4817
4818 if (const auto arr = dyn_cast<ConstantArrayType>(QT.getTypePtr())) {
4819 QualType newQT= ReSubstTemplateArg(arr->getElementType(),instance);
4820
4821 if (newQT == arr->getElementType()) return QT;
4822 QT = Ctxt.getConstantArrayType(newQT,
4823 arr->getSize(),
4824 arr->getSizeExpr(),
4825 arr->getSizeModifier(),
4826 arr->getIndexTypeCVRQualifiers());
4827
4828 } else if (const auto arr = dyn_cast<DependentSizedArrayType>(QT.getTypePtr())) {
4829 QualType newQT = ReSubstTemplateArg(arr->getElementType(),instance);
4830
4831 if (newQT == QT) return QT;
4832 QT = Ctxt.getDependentSizedArrayType (newQT,
4833 arr->getSizeExpr(),
4834 arr->getSizeModifier(),
4835 arr->getIndexTypeCVRQualifiers());
4836
4837 } else if (const auto arr = dyn_cast<IncompleteArrayType>(QT.getTypePtr())) {
4838 QualType newQT = ReSubstTemplateArg(arr->getElementType(),instance);
4839
4840 if (newQT == arr->getElementType()) return QT;
4841 QT = Ctxt.getIncompleteArrayType (newQT,
4842 arr->getSizeModifier(),
4843 arr->getIndexTypeCVRQualifiers());
4844
4845 } else if (const auto arr = dyn_cast<VariableArrayType>(QT.getTypePtr())) {
4846 QualType newQT = ReSubstTemplateArg(arr->getElementType(),instance);
4847
4848 if (newQT == arr->getElementType()) return QT;
4849 QT = Ctxt.getVariableArrayType (newQT,
4850 arr->getSizeExpr(),
4851 arr->getSizeModifier(),
4852 arr->getIndexTypeCVRQualifiers());
4853 }
4854
4855 // Add back the qualifiers.
4856 QT = Ctxt.getQualifiedType(QT, quals);
4857 return QT;
4858 }
4859
4860 const clang::TemplateSpecializationType* TST
4861 = llvm::dyn_cast<const clang::TemplateSpecializationType>(instance);
4862
4863 if (!TST) return input;
4864
4865 const clang::ClassTemplateSpecializationDecl* TSTdecl
4866 = llvm::dyn_cast_or_null<const clang::ClassTemplateSpecializationDecl>(instance->getAsCXXRecordDecl());
4867
4868 if (!TSTdecl) return input;
4869
4870 const clang::SubstTemplateTypeParmType *substType
4871 = llvm::dyn_cast<clang::SubstTemplateTypeParmType>(input.getTypePtr());
4872
4873 if (substType) {
4874 // Make sure it got replaced from this template
4875 const clang::ClassTemplateDecl *replacedCtxt = nullptr;
4876
4877 const clang::DeclContext *replacedDeclCtxt = substType->getReplacedParameter()->getDeclContext();
4878 const clang::CXXRecordDecl *decl = llvm::dyn_cast<clang::CXXRecordDecl>(replacedDeclCtxt);
4879 unsigned int index = substType->getReplacedParameter()->getIndex();
4880 if (decl) {
4881
4882 if (decl->getKind() == clang::Decl::ClassTemplatePartialSpecialization) {
4883 const clang::ClassTemplatePartialSpecializationDecl *spec = llvm::dyn_cast<clang::ClassTemplatePartialSpecializationDecl>(decl);
4884
4885 unsigned int depth = substType->getReplacedParameter()->getDepth();
4886
4887 const TemplateArgument *instanceArgs = spec->getTemplateArgs().data();
4888 unsigned int instanceNArgs = spec->getTemplateArgs().size();
4889
4890 // Search for the 'right' replacement.
4891
4892 for(unsigned int A = 0; A < instanceNArgs; ++A) {
4893 if (instanceArgs[A].getKind() == clang::TemplateArgument::Type) {
4894 clang::QualType argQualType = instanceArgs[A].getAsType();
4895
4896 const clang::TemplateTypeParmType *replacementType;
4897
4898 replacementType = llvm::dyn_cast<clang::TemplateTypeParmType>(argQualType);
4899
4900 if (!replacementType) {
4901 const clang::SubstTemplateTypeParmType *argType
4902 = llvm::dyn_cast<clang::SubstTemplateTypeParmType>(argQualType);
4903 if (argType) {
4904 clang::QualType replacementQT = argType->getReplacementType();
4905 replacementType = llvm::dyn_cast<clang::TemplateTypeParmType>(replacementQT);
4906 }
4907 }
4908 if (replacementType &&
4909 depth == replacementType->getDepth() &&
4910 index == replacementType->getIndex() )
4911 {
4912 index = A;
4913 break;
4914 }
4915 }
4916 }
4917 replacedCtxt = spec->getSpecializedTemplate();
4918 } else {
4919 replacedCtxt = decl->getDescribedClassTemplate();
4920 }
4921 } else if (auto const declguide = llvm::dyn_cast<clang::CXXDeductionGuideDecl>(replacedDeclCtxt)) {
4922 replacedCtxt = llvm::dyn_cast<clang::ClassTemplateDecl>(declguide->getDeducedTemplate());
4923 } else if (auto const ctdecl = llvm::dyn_cast<clang::ClassTemplateDecl>(replacedDeclCtxt)) {
4925 } else {
4926 std::string astDump;
4927 llvm::raw_string_ostream ostream(astDump);
4928 instance->dump(ostream, Ctxt);
4929 ostream.flush();
4930 ROOT::TMetaUtils::Warning("ReSubstTemplateArg","Unexpected type of declaration context for template parameter: %s.\n\tThe responsible class is:\n\t%s\n",
4931 replacedDeclCtxt->getDeclKindName(), astDump.c_str());
4932 replacedCtxt = nullptr;
4933 }
4934
4935 if (replacedCtxt && replacedCtxt->getCanonicalDecl() == TSTdecl->getSpecializedTemplate()->getCanonicalDecl())
4936 {
4937 const auto &TAs = TST->template_arguments();
4938 if (index >= TAs.size()) {
4939 // The argument replaced was a default template argument that is
4940 // being listed as part of the instance ...
4941 // so we probably don't really know how to spell it ... we would need to recreate it
4942 // (See AddDefaultParameters).
4943 return input;
4944 } else if (TAs[index].getKind() == clang::TemplateArgument::Type) {
4945 return TAs[index].getAsType();
4946 } else {
4947 // The argument is (likely) a value or expression and there is nothing for us
4948 // to change
4949 return input;
4950 }
4951 }
4952 }
4953 // Maybe a class template instance, recurse and rebuild
4954 const clang::TemplateSpecializationType* inputTST
4955 = llvm::dyn_cast<const clang::TemplateSpecializationType>(input.getTypePtr());
4956 const clang::ASTContext& astCtxt = TSTdecl->getASTContext();
4957
4958 if (inputTST) {
4959 bool mightHaveChanged = false;
4960 llvm::SmallVector<clang::TemplateArgument, 4> desArgs;
4961 for (const clang::TemplateArgument &TA : inputTST->template_arguments()) {
4962 if (TA.getKind() != clang::TemplateArgument::Type) {
4963 desArgs.push_back(TA);
4964 continue;
4965 }
4966
4967 clang::QualType SubTy = TA.getAsType();
4968 // Check if the type needs more desugaring and recurse.
4969 if (SubTy->getPrefix() // LLVM22: was isa<ElaboratedType>
4970 || llvm::isa<clang::SubstTemplateTypeParmType>(SubTy)
4971 || llvm::isa<clang::TemplateSpecializationType>(SubTy)) {
4972 clang::QualType newSubTy = ReSubstTemplateArg(SubTy,instance);
4974 if (!newSubTy.isNull()) {
4975 desArgs.push_back(clang::TemplateArgument(newSubTy));
4976 }
4977 } else
4978 desArgs.push_back(TA);
4979 }
4980
4981 // If desugaring happened allocate new type in the AST.
4982 if (mightHaveChanged) {
4983 clang::Qualifiers qualifiers = input.getLocalQualifiers();
4984 input = astCtxt.getTemplateSpecializationType(inputTST->getKeyword(),
4985 inputTST->getTemplateName(),
4986 desArgs,
4987 /*CanonicalArgs=*/{},
4988 inputTST->getCanonicalTypeInternal());
4989 input = astCtxt.getQualifiedType(input, qualifiers);
4990 }
4991 }
4992
4993 return input;
4994}
4995
4996////////////////////////////////////////////////////////////////////////////////
4997/// Remove the last n template arguments from the name
4998
5000{
5001 if ( nArgsToRemove == 0 || name == "")
5002 return 0;
5003
5004 // We proceed from the right to the left, counting commas which are not
5005 // enclosed by < >.
5006 const unsigned int length = name.length();
5007 unsigned int cur=0; // let's start beyond the first > from the right
5008 unsigned int nArgsRemoved=0;
5009 unsigned int nBraces=0;
5010 char c='@';
5012 c = name[cur];
5013 if (c == '<') nBraces++;
5014 if (c == '>') nBraces--;
5015 if (c == ',' && nBraces==1 /*So we are not in a sub-template*/) nArgsRemoved++;
5016 cur++;
5017 }
5018 cur--;
5019 name = name.substr(0,cur)+">";
5020 return 0;
5021
5022}
5023
5024////////////////////////////////////////////////////////////////////////////////
5025/// Converts STL container name to number. vector -> 1, etc..
5026
5028{
5029 static const char *stls[] = //container names
5030 {"any","vector","list", "deque","map","multimap","set","multiset","bitset",
5031 "forward_list","unordered_set","unordered_multiset","unordered_map","unordered_multimap", "RVec", nullptr};
5032 static const ROOT::ESTLType values[] =
5043 };
5044 // kind of stl container
5045 for (int k = 1; stls[k]; k++) {
5046 if (type == stls[k])
5047 return values[k];
5048 }
5049 return ROOT::kNotSTL;
5050}
5051
5052////////////////////////////////////////////////////////////////////////////////
5053
5054const clang::TypedefNameDecl *ROOT::TMetaUtils::GetAnnotatedRedeclarable(const clang::TypedefNameDecl *TND)
5055{
5056 if (!TND)
5057 return nullptr;
5058
5059 TND = TND->getMostRecentDecl();
5060 while (TND && !(TND->hasAttrs()))
5061 TND = TND->getPreviousDecl();
5062
5063 return TND;
5064}
5065
5066////////////////////////////////////////////////////////////////////////////////
5067
5068const clang::TagDecl *ROOT::TMetaUtils::GetAnnotatedRedeclarable(const clang::TagDecl *TD)
5069{
5070 if (!TD)
5071 return nullptr;
5072
5073 TD = TD->getMostRecentDecl();
5074 while (TD && !(TD->hasAttrs() && TD->isThisDeclarationADefinition()))
5075 TD = TD->getPreviousDecl();
5076
5077 return TD;
5078}
5079
5080////////////////////////////////////////////////////////////////////////////////
5081/// Extract the immediately outer namespace and then launch the recursion
5082
5084 std::list<std::pair<std::string,bool> >& enclosingNamespaces)
5085{
5086 const clang::DeclContext* enclosingNamespaceDeclCtxt = decl.getDeclContext();
5087 if (!enclosingNamespaceDeclCtxt) return;
5088
5089 const clang::NamespaceDecl* enclosingNamespace =
5090 clang::dyn_cast<clang::NamespaceDecl>(enclosingNamespaceDeclCtxt);
5091 if (!enclosingNamespace) return;
5092
5093 enclosingNamespaces.push_back(std::make_pair(enclosingNamespace->getNameAsString(),
5094 enclosingNamespace->isInline()));
5095
5097
5098}
5099
5100////////////////////////////////////////////////////////////////////////////////
5101/// Extract enclosing namespaces recursively
5102
5104 std::list<std::pair<std::string,bool> >& enclosingNamespaces)
5105{
5106 const clang::DeclContext* enclosingNamespaceDeclCtxt = ctxt.getParent ();
5107
5108 // If no parent is found, nothing more to be done
5110 return;
5111 }
5112
5113 // Check if the parent is a namespace (it could be a class for example)
5114 // if not, nothing to be done here
5115 const clang::NamespaceDecl* enclosingNamespace = clang::dyn_cast<clang::NamespaceDecl>(enclosingNamespaceDeclCtxt);
5116 if (!enclosingNamespace) return;
5117
5118 // Add to the list of parent namespaces
5119 enclosingNamespaces.push_back(std::make_pair(enclosingNamespace->getNameAsString(),
5120 enclosingNamespace->isInline()));
5121
5122 // here the recursion
5124}
5125
5126////////////////////////////////////////////////////////////////////////////////
5127/// Extract the names and types of containing scopes.
5128/// Stop if a class is met and return its pointer.
5129
5130const clang::RecordDecl *ROOT::TMetaUtils::ExtractEnclosingScopes(const clang::Decl& decl,
5131 std::list<std::pair<std::string,unsigned int> >& enclosingSc)
5132{
5133 const clang::DeclContext* enclosingDeclCtxt = decl.getDeclContext();
5134 if (!enclosingDeclCtxt) return nullptr;
5135
5136 unsigned int scopeType;
5137
5138 if (auto enclosingNamespacePtr =
5139 clang::dyn_cast<clang::NamespaceDecl>(enclosingDeclCtxt)){
5140 scopeType= enclosingNamespacePtr->isInline() ? 1 : 0; // inline or simple namespace
5141 enclosingSc.push_back(std::make_pair(enclosingNamespacePtr->getNameAsString(),scopeType));
5143 }
5144
5145 if (auto enclosingClassPtr =
5146 clang::dyn_cast<clang::RecordDecl>(enclosingDeclCtxt)){
5147 return enclosingClassPtr;
5148 }
5149
5150 return nullptr;
5151}
5152
5153////////////////////////////////////////////////////////////////////////////////
5154/// Reimplementation of TSystem::ExpandPathName() that cannot be
5155/// used from TMetaUtils.
5156
5157static void replaceEnvVars(const char* varname, std::string& txt)
5158{
5159 std::string::size_type beginVar = 0;
5160 std::string::size_type endVar = 0;
5161 while ((beginVar = txt.find('$', beginVar)) != std::string::npos
5162 && beginVar + 1 < txt.length()) {
5163 std::string::size_type beginVarName = beginVar + 1;
5164 std::string::size_type endVarName = std::string::npos;
5165 if (txt[beginVarName] == '(') {
5166 // "$(VARNAME)" style.
5167 endVarName = txt.find(')', beginVarName);
5168 ++beginVarName;
5169 if (endVarName == std::string::npos) {
5170 ROOT::TMetaUtils::Error(nullptr, "Missing ')' for '$(' in $%s at %s\n",
5171 varname, txt.c_str() + beginVar);
5172 return;
5173 }
5174 endVar = endVarName + 1;
5175 } else {
5176 // "$VARNAME/..." style.
5177 beginVarName = beginVar + 1;
5179 while (isalnum(txt[endVarName]) || txt[endVarName] == '_')
5180 ++endVarName;
5182 }
5183
5184 const char* val = std::getenv(txt.substr(beginVarName,
5185 endVarName - beginVarName).c_str());
5186 if (!val) val = "";
5187
5188 txt.replace(beginVar, endVar - beginVar, val);
5189 int lenval = strlen(val);
5190 int delta = lenval - (endVar - beginVar); // these many extra chars,
5191 endVar += delta; // advance the end marker accordingly.
5192
5193 // Look for the next one
5194 beginVar = endVar + 1;
5195 }
5196}
5197
5198////////////////////////////////////////////////////////////////////////////////
5199/// Organise the parameters for cling in order to guarantee relocatability
5200/// It treats the gcc toolchain and the root include path
5201/// FIXME: enables relocatability for experiments' framework headers until PCMs
5202/// are available.
5203
5205{
5206 const char* envInclPath = std::getenv("ROOT_INCLUDE_PATH");
5207
5208 if (!envInclPath)
5209 return;
5210
5211#ifdef _WIN32
5212 constexpr char kPathSep = ';';
5213#else
5214 constexpr char kPathSep = ':';
5215#endif
5216
5217 std::istringstream envInclPathsStream(envInclPath);
5218 std::string inclPath;
5219 while (std::getline(envInclPathsStream, inclPath, kPathSep)) {
5220 // Can't use TSystem in here; re-implement TSystem::ExpandPathName().
5221 replaceEnvVars("ROOT_INCLUDE_PATH", inclPath);
5222 if (!inclPath.empty()) {
5223 clingArgs.push_back("-I");
5224 clingArgs.push_back(inclPath);
5225 }
5226 }
5227}
5228
5229////////////////////////////////////////////////////////////////////////////////
5230
5231void ROOT::TMetaUtils::ReplaceAll(std::string& str, const std::string& from, const std::string& to,bool recurse)
5232{
5233 if(from.empty())
5234 return;
5235 size_t start_pos = 0;
5236 bool changed=true;
5237 while (changed){
5238 changed=false;
5239 start_pos = 0;
5240 while((start_pos = str.find(from, start_pos)) != std::string::npos) {
5241 str.replace(start_pos, from.length(), to);
5242 start_pos += to.length();
5243 if (recurse) changed = true;
5244 }
5245 }
5246}
5247
5248////////////////////////////////////////////////////////////////////////////////
5249/// Return the separator suitable for this platform.
5254
5255////////////////////////////////////////////////////////////////////////////////
5256
5257bool ROOT::TMetaUtils::EndsWith(const std::string &theString, const std::string &theSubstring)
5258{
5259 if (theString.size() < theSubstring.size()) return false;
5260 const unsigned int theSubstringSize = theSubstring.size();
5261 return 0 == theString.compare(theString.size() - theSubstringSize,
5263 theSubstring);
5264}
5265
5266////////////////////////////////////////////////////////////////////////////////
5267
5268bool ROOT::TMetaUtils::BeginsWith(const std::string &theString, const std::string &theSubstring)
5269{
5270 if (theString.size() < theSubstring.size()) return false;
5271 const unsigned int theSubstringSize = theSubstring.size();
5272 return 0 == theString.compare(0,
5274 theSubstring);
5275}
5276
5277
5278
5279////////////////////////////////////////////////////////////////////////////////
5280
5282{
5283 // Note, should change this into take llvm::StringRef.
5284
5285 if ((strstr(filename, "LinkDef") || strstr(filename, "Linkdef") ||
5286 strstr(filename, "linkdef")) && strstr(filename, ".h")) {
5287 return true;
5288 }
5289 size_t len = strlen(filename);
5290 size_t linkdeflen = 9; /* strlen("linkdef.h") */
5291 if (len >= 9) {
5292 if (0 == strncasecmp(filename + (len - linkdeflen), "linkdef", linkdeflen - 2)
5293 && 0 == strcmp(filename + (len - 2), ".h")
5294 ) {
5295 return true;
5296 } else {
5297 return false;
5298 }
5299 } else {
5300 return false;
5301 }
5302}
5303
5304////////////////////////////////////////////////////////////////////////////////
5305
5307{
5308 return llvm::sys::path::extension(filename) == ".h" ||
5309 llvm::sys::path::extension(filename) == ".hh" ||
5310 llvm::sys::path::extension(filename) == ".hpp" ||
5311 llvm::sys::path::extension(filename) == ".H" ||
5312 llvm::sys::path::extension(filename) == ".h++" ||
5313 llvm::sys::path::extension(filename) == "hxx" ||
5314 llvm::sys::path::extension(filename) == "Hxx" ||
5315 llvm::sys::path::extension(filename) == "HXX";
5316}
5317
5318////////////////////////////////////////////////////////////////////////////////
5319
5320const std::string ROOT::TMetaUtils::AST2SourceTools::Decls2FwdDecls(const std::vector<const clang::Decl *> &decls,
5321 cling::Interpreter::IgnoreFilesFunc_t ignoreFiles,
5322 const cling::Interpreter &interp,
5323 std::string *logs)
5324{
5325 clang::Sema &sema = interp.getSema();
5326 cling::Transaction theTransaction(sema);
5327 std::set<clang::Decl *> addedDecls;
5328 for (auto decl : decls) {
5329 // again waiting for cling
5330 clang::Decl *ncDecl = const_cast<clang::Decl *>(decl);
5331 theTransaction.append(ncDecl);
5332 }
5333 std::string newFwdDecl;
5334 llvm::raw_string_ostream llvmOstr(newFwdDecl);
5335
5336 std::string locallogs;
5337 llvm::raw_string_ostream llvmLogStr(locallogs);
5338 interp.forwardDeclare(theTransaction, sema.getPreprocessor(), sema.getASTContext(), llvmOstr, true,
5339 logs ? &llvmLogStr : nullptr, ignoreFiles);
5340 llvmOstr.flush();
5341 llvmLogStr.flush();
5342 if (logs)
5343 logs->swap(locallogs);
5344 return newFwdDecl;
5345}
5346
5347////////////////////////////////////////////////////////////////////////////////
5348/// Take the namespaces which enclose the decl and put them around the
5349/// definition string.
5350/// For example, if the definition string is "myClass" which is enclosed by
5351/// the namespaces ns1 and ns2, one would get:
5352/// namespace ns2{ namespace ns1 { class myClass; } }
5353
5355 std::string& defString)
5356{
5358 return rcd ? 1:0;
5359}
5360
5361////////////////////////////////////////////////////////////////////////////////
5362/// Take the scopes which enclose the decl and put them around the
5363/// definition string.
5364/// If a class is encountered, bail out.
5365
5366const clang::RecordDecl* ROOT::TMetaUtils::AST2SourceTools::EncloseInScopes(const clang::Decl& decl,
5367 std::string& defString)
5368{
5369 std::list<std::pair<std::string,unsigned int> > enclosingNamespaces;
5371
5372 if (rcdPtr) return rcdPtr;
5373
5374 // Check if we have enclosing namespaces
5375 static const std::string scopeType [] = {"namespace ", "inline namespace ", "class "};
5376
5377 std::string scopeName;
5378 std::string scopeContent;
5379 unsigned int scopeIndex;
5380 for (auto const & encScope : enclosingNamespaces){
5381 scopeIndex = encScope.second;
5382 scopeName = encScope.first;
5383 scopeContent = " { " + defString + " }";
5385 scopeName +
5387 }
5388 return nullptr;
5389}
5390
5391////////////////////////////////////////////////////////////////////////////////
5392/// Loop over the template parameters and build a string for template arguments
5393/// using the fully qualified name
5394/// There are different cases:
5395/// Case 1: a simple template parameter
5396/// E.g. `template<typename T> class A;`
5397/// Case 2: a non-type: either an integer or an enum
5398/// E.g. `template<int I, Foo > class A;` where `Foo` is `enum Foo {red, blue};`
5399/// 2 sub cases here:
5400/// SubCase 2.a: the parameter is an enum: bail out, cannot be treated.
5401/// SubCase 2.b: use the fully qualified name
5402/// Case 3: a TemplateTemplate argument
5403/// E.g. `template <template <typename> class T> class container { };`
5404
5406 const clang::TemplateParameterList& tmplParamList,
5407 const cling::Interpreter& interpreter)
5408{
5409 templateArgs="<";
5410 for (auto prmIt = tmplParamList.begin();
5411 prmIt != tmplParamList.end(); prmIt++){
5412
5413 if (prmIt != tmplParamList.begin())
5414 templateArgs += ", ";
5415
5416 auto nDecl = *prmIt;
5417 std::string typeName;
5418
5419 // Case 1
5420 if (llvm::isa<clang::TemplateTypeParmDecl>(nDecl)){
5421 typeName = "typename ";
5422 if (nDecl->isParameterPack())
5423 typeName += "... ";
5424 typeName += (*prmIt)->getNameAsString();
5425 }
5426 // Case 2
5427 else if (auto nttpd = llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(nDecl)){
5428 auto theType = nttpd->getType();
5429 // If this is an enum, use int as it is impossible to fwd declare and
5430 // this makes sense since it is not a type...
5431 if (theType.getAsString().find("enum") != std::string::npos){
5432 std::string astDump;
5433 llvm::raw_string_ostream ostream(astDump);
5434 nttpd->dump(ostream);
5435 ostream.flush();
5436 ROOT::TMetaUtils::Warning(nullptr,"Forward declarations of templates with enums as template parameters. The responsible class is: %s\n", astDump.c_str());
5437 return 1;
5438 } else {
5440 theType,
5441 interpreter);
5442 }
5443 }
5444 // Case 3: TemplateTemplate argument
5445 else if (auto ttpd = llvm::dyn_cast<clang::TemplateTemplateParmDecl>(nDecl)){
5447 if (retCode!=0){
5448 std::string astDump;
5449 llvm::raw_string_ostream ostream(astDump);
5450 ttpd->dump(ostream);
5451 ostream.flush();
5452 ROOT::TMetaUtils::Error(nullptr,"Cannot reconstruct template template parameter forward declaration for %s\n", astDump.c_str());
5453 return 1;
5454 }
5455 }
5456
5457 templateArgs += typeName;
5458 }
5459
5460 templateArgs+=">";
5461 return 0;
5462}
5463
5464////////////////////////////////////////////////////////////////////////////////
5465/// Convert a tmplt decl to its fwd decl
5466
5468 const cling::Interpreter& interpreter,
5469 std::string& defString)
5470{
5471 std::string templatePrefixString;
5472 auto tmplParamList= templDecl.getTemplateParameters();
5473 if (!tmplParamList){ // Should never happen
5474 Error(nullptr,
5475 "Cannot extract template parameter list for %s",
5476 templDecl.getNameAsString().c_str());
5477 return 1;
5478 }
5479
5481 if (retCode!=0){
5482 Warning(nullptr,
5483 "Problems with arguments for forward declaration of class %s\n",
5484 templDecl.getNameAsString().c_str());
5485 return retCode;
5486 }
5487 templatePrefixString = "template " + templatePrefixString + " ";
5488
5489 defString = templatePrefixString + "class ";
5490 if (templDecl.isParameterPack())
5491 defString += "... ";
5492 defString += templDecl.getNameAsString();
5493 if (llvm::isa<clang::TemplateTemplateParmDecl>(&templDecl)) {
5494 // When fwd declaring the template template arg of
5495 // namespace N { template <template <class T> class C> class X; }
5496 // we don't need to put it into any namespace, and we want no trailing
5497 // ';'
5498 return 0;
5499 }
5500 defString += ';';
5502}
5503
5504////////////////////////////////////////////////////////////////////////////////
5505
5506static int TreatSingleTemplateArg(const clang::TemplateArgument& arg,
5507 std::string& argFwdDecl,
5508 const cling::Interpreter& interpreter,
5509 bool acceptStl=false)
5510{
5511 using namespace ROOT::TMetaUtils::AST2SourceTools;
5512
5513 // We do nothing in presence of ints, bools, templates.
5514 // We should probably in presence of templates though...
5515 if (clang::TemplateArgument::Type != arg.getKind()) return 0;
5516
5517 auto argQualType = arg.getAsType();
5518
5519 // Recursively remove all *
5520 while (llvm::isa<clang::PointerType>(argQualType.getTypePtr())) argQualType = argQualType->getPointeeType();
5521
5522 auto argTypePtr = argQualType.getTypePtr();
5523
5524 // Bail out on enums
5525 if (llvm::isa<clang::EnumType>(argTypePtr)){
5526 return 1;
5527 }
5528
5529 // If this is a built-in, just return: fwd decl not necessary.
5530 if (llvm::isa<clang::BuiltinType>(argTypePtr)){
5531 return 0;
5532 }
5533
5534 // Treat typedefs which are arguments
5535 if (auto tdTypePtr = llvm::dyn_cast<clang::TypedefType>(argTypePtr)) {
5536 FwdDeclFromTypeDefNameDecl(*tdTypePtr->getDecl(), interpreter, argFwdDecl);
5537 return 0;
5538 }
5539
5540 if (auto argRecTypePtr = llvm::dyn_cast<clang::RecordType>(argTypePtr)){
5541 // Now we cannot but have a RecordType
5542 if (auto argRecDeclPtr = argRecTypePtr->getDecl()){
5543 FwdDeclFromRcdDecl(*argRecDeclPtr,interpreter,argFwdDecl,acceptStl);
5544 }
5545 return 0;
5546 }
5547
5548 return 1;
5549}
5550
5551////////////////////////////////////////////////////////////////////////////////
5552/// Convert a tmplt decl to its fwd decl
5553
5555 const cling::Interpreter& interpreter,
5556 std::string& defString,
5557 const std::string &normalizedName)
5558{
5559 // If this is an explicit specialization, inject it into cling, too, such that it can have
5560 // externalLexicalStorage, see TCling.cxx's ExtVisibleStorageAdder::VisitClassTemplateSpecializationDecl.
5561 if (auto tmplSpecDeclPtr = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(&recordDecl)) {
5562 if (const auto *specDef = tmplSpecDeclPtr->getDefinition()) {
5563 if (specDef->getTemplateSpecializationKind() != clang::TSK_ExplicitSpecialization)
5564 return 0;
5565 // normalizedName contains scope, no need to enclose in namespace!
5567 std::cout << " Forward declaring template spec " << normalizedName << ":\n";
5568 for (auto arg : tmplSpecDeclPtr->getTemplateArgs().asArray()) {
5569 std::string argFwdDecl;
5570 int retCode = TreatSingleTemplateArg(arg, argFwdDecl, interpreter, /*acceptStl=*/false);
5572 std::cout << " o Template argument ";
5573 if (retCode == 0) {
5574 std::cout << "successfully treated. Arg fwd decl: " << argFwdDecl << std::endl;
5575 } else {
5576 std::cout << "could not be treated. Abort fwd declaration generation.\n";
5577 }
5578 }
5579
5580 if (retCode != 0) { // A sign we must bail out
5581 return retCode;
5582 }
5583 defString += argFwdDecl + '\n';
5584 }
5585 defString += "template <> class " + normalizedName + ';';
5586 return 0;
5587 }
5588 }
5589
5590 return 0;
5591}
5592
5593////////////////////////////////////////////////////////////////////////////////
5594/// Convert a rcd decl to its fwd decl
5595/// If this is a template specialisation, treat in the proper way.
5596/// If it is contained in a class, just fwd declare the class.
5597
5599 const cling::Interpreter& interpreter,
5600 std::string& defString,
5601 bool acceptStl)
5602{
5603 // Do not fwd declare the templates in the stl.
5605 return 0;
5606
5607 // Do not fwd declare unnamed decls.
5608 if (!recordDecl.getIdentifier())
5609 return 0;
5610
5611 // We may need to fwd declare the arguments of the template
5612 std::string argsFwdDecl;
5613
5614 if (auto tmplSpecDeclPtr = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(&recordDecl)){
5615 std::string argFwdDecl;
5617 std::cout << "Class " << recordDecl.getNameAsString()
5618 << " is a template specialisation. Treating its arguments.\n";
5619 for(auto arg : tmplSpecDeclPtr->getTemplateArgs().asArray()){
5622 std::cout << " o Template argument ";
5623 if (retCode==0){
5624 std::cout << "successfully treated. Arg fwd decl: " << argFwdDecl << std::endl;
5625 } else {
5626 std::cout << "could not be treated. Abort fwd declaration generation.\n";
5627 }
5628 }
5629
5630 if (retCode!=0){ // A sign we must bail out
5631 return retCode;
5632 }
5634 }
5635
5636 if (acceptStl){
5638 return 0;
5639 }
5640
5641 int retCode=0;
5642 if (auto tmplDeclPtr = tmplSpecDeclPtr->getSpecializedTemplate()){
5644 }
5645 defString = argsFwdDecl + "\n" + defString;
5646 return retCode;
5647
5648 }
5649
5650 defString = "class " + recordDecl.getNameAsString() + ";";
5651 const clang::RecordDecl* rcd = EncloseInScopes(recordDecl, defString);
5652
5653 if (rcd){
5655 }
5656 // Add a \n here to avoid long lines which contain duplications, for example (from MathCore):
5657 // namespace ROOT { namespace Math { class IBaseFunctionMultiDim; } }namespace ROOT { namespace Fit { template <typename FunType> class Chi2FCN; } }
5658 // namespace ROOT { namespace Math { class IGradientFunctionMultiDim; } }namespace ROOT { namespace Fit { template <typename FunType> class Chi2FCN; } }
5659 defString = argsFwdDecl + "\n" + defString;
5660
5661 return 0;
5662}
5663
5664////////////////////////////////////////////////////////////////////////////////
5665/// Extract "forward declaration" of a typedef.
5666/// If the typedef is contained in a class, just fwd declare the class.
5667/// If not, fwd declare the typedef and all the dependent typedefs and types if necessary.
5668
5670 const cling::Interpreter& interpreter,
5671 std::string& fwdDeclString,
5672 std::unordered_set<std::string>* fwdDeclSetPtr)
5673{
5674 std::string buffer = tdnDecl.getNameAsString();
5675 std::string underlyingName;
5676 auto underlyingType = tdnDecl.getUnderlyingType().getCanonicalType();
5677 if (const clang::TagType* TT
5678 = llvm::dyn_cast<clang::TagType>(underlyingType.getTypePtr())) {
5679 if (clang::NamedDecl* ND = TT->getDecl()) {
5680 if (!ND->getIdentifier()) {
5681 // No fwd decl for unnamed underlying entities.
5682 return 0;
5683 }
5684 }
5685 }
5686
5687 TNormalizedCtxt nCtxt(interpreter.getLookupHelper());
5691 nCtxt);
5692
5693 // Heuristic: avoid entities like myclass<myType1, myType2::xyz>
5694 if (underlyingName.find(">::") != std::string::npos)
5695 return 0;
5696
5697 buffer="typedef "+underlyingName+" "+buffer+";";
5698 const clang::RecordDecl* rcd=EncloseInScopes(tdnDecl,buffer);
5699 if (rcd) {
5700 // We do not need the whole series of scopes, just the class.
5701 // It is enough to trigger an uncomplete type autoload/parse callback
5702 // for example: MyClass::blabla::otherNs::myTypedef
5704 }
5705
5706 // Start Recursion if the underlying type is a TypedefNameDecl
5707 // Note: the simple cast w/o the getSingleStepDesugaredType call
5708 // does not work in case the typedef is in a namespace.
5709 auto& ctxt = tdnDecl.getASTContext();
5710 auto immediatelyUnderlyingType = underlyingType.getSingleStepDesugaredType(ctxt);
5711
5712 if (auto underlyingTdnTypePtr = llvm::dyn_cast<clang::TypedefType>(immediatelyUnderlyingType.getTypePtr())){
5713 std::string tdnFwdDecl;
5717 tdnFwdDecl,
5719 if (!fwdDeclSetPtr || fwdDeclSetPtr->insert(tdnFwdDecl).second)
5721 } else if (auto CXXRcdDeclPtr = immediatelyUnderlyingType->getAsCXXRecordDecl()){
5722 std::string classFwdDecl;
5724 std::cout << "Typedef " << tdnDecl.getNameAsString() << " hides a class: "
5725 << CXXRcdDeclPtr->getNameAsString() << std::endl;
5729 true /* acceptStl*/);
5730 if (retCode!=0){ // bail out
5731 return 0;
5732 }
5733
5734 if (!fwdDeclSetPtr || fwdDeclSetPtr->insert(classFwdDecl).second)
5736 }
5737
5738 fwdDeclString+=buffer;
5739
5740 return 0;
5741}
5742
5743////////////////////////////////////////////////////////////////////////////////
5744/// Get the default value as string.
5745/// Limited at the moment to:
5746/// - Integers
5747/// - Booleans
5748
5749int ROOT::TMetaUtils::AST2SourceTools::GetDefArg(const clang::ParmVarDecl& par,
5750 std::string& valAsString,
5751 const clang::PrintingPolicy& ppolicy)
5752{
5753 auto defArgExprPtr = par.getDefaultArg();
5754 auto& ctxt = par.getASTContext();
5755 if(!defArgExprPtr->isEvaluatable(ctxt)){
5756 return -1;
5757 }
5758
5759 auto defArgType = par.getType();
5760
5761 // The value is a boolean
5762 if (defArgType->isBooleanType()){
5763 bool result;
5764 defArgExprPtr->EvaluateAsBooleanCondition (result,ctxt);
5765 valAsString=std::to_string(result);
5766 return 0;
5767 }
5768
5769 // The value is an integer
5770 if (defArgType->isIntegerType()){
5771 clang::Expr::EvalResult evalResult;
5772 defArgExprPtr->EvaluateAsInt(evalResult, ctxt);
5773 llvm::APSInt result = evalResult.Val.getInt();
5774 auto uintVal = *result.getRawData();
5775 if (result.isNegative()){
5776 long long int intVal=uintVal*-1;
5777 valAsString=std::to_string(intVal);
5778 } else {
5779 valAsString=std::to_string(uintVal);
5780 }
5781
5782 return 0;
5783 }
5784
5785 // The value is something else. We go for the generalised printer
5786 llvm::raw_string_ostream rso(valAsString);
5787 defArgExprPtr->printPretty(rso,nullptr,ppolicy);
5788 valAsString = rso.str();
5789 // We can be in presence of a string. Let's escape the characters properly.
5790 ROOT::TMetaUtils::ReplaceAll(valAsString,"\\\"","__TEMP__VAL__");
5792 ROOT::TMetaUtils::ReplaceAll(valAsString,"__TEMP__VAL__","\\\"");
5793
5794 return 0;
5795}
5796
The file contains utilities which are foundational and could be used across the core component of ROO...
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
#define R(a, b, c, d, e, f, g, h, i)
Definition RSha256.hxx:110
static Roo_reg_AGKInteg1D instance
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
short Version_t
Class version identifier (short)
Definition RtypesCore.h:80
static void indent(ostringstream &buf, int indent_level)
static bool RecurseKeepNParams(clang::TemplateArgument &normTArg, const clang::TemplateArgument &tArg, const cling::Interpreter &interp, const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt, const clang::ASTContext &astCtxt)
static clang::SourceLocation getFinalSpellingLoc(clang::SourceManager &sourceManager, clang::SourceLocation sourceLoc)
const clang::DeclContext * GetEnclosingSpace(const clang::RecordDecl &cl)
bool IsTemplate(const clang::Decl &cl)
static void KeepNParams(clang::QualType &normalizedType, const clang::QualType &vanillaType, const cling::Interpreter &interp, const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt)
This function allows to manipulate the number of arguments in the type of a template specialisation.
static void CreateNameTypeMap(const clang::CXXRecordDecl &cl, ROOT::MembersTypeMap_t &nameType)
Create the data member name-type map for given class.
const clang::CXXMethodDecl * GetMethodWithProto(const clang::Decl *cinfo, const char *method, const char *proto, const cling::Interpreter &interp, bool diagnose)
int dumpDeclForAssert(const clang::Decl &D, const char *commentStart)
static void replaceEnvVars(const char *varname, std::string &txt)
Reimplementation of TSystem::ExpandPathName() that cannot be used from TMetaUtils.
static bool areEqualValues(const clang::TemplateArgument &tArg, const clang::NamedDecl &tPar)
std::cout << "Are equal values?\n";
static bool isTypeWithDefault(const clang::NamedDecl *nDecl)
Check if this NamedDecl is a template parameter with a default argument.
static int TreatSingleTemplateArg(const clang::TemplateArgument &arg, std::string &argFwdDecl, const cling::Interpreter &interpreter, bool acceptStl=false)
static bool areEqualTypes(const clang::TemplateArgument &tArg, llvm::SmallVectorImpl< clang::TemplateArgument > &preceedingTArgs, const clang::NamedDecl &tPar, const cling::Interpreter &interp, const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt)
static bool hasSomeTypedefSomewhere(const clang::Type *T)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:208
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:252
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void input
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t dest
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char filename
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h length
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t UChar_t len
Option_t Option_t TPoint TPoint const char mode
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
char name[80]
Definition TGX11.cxx:148
const char * proto
Definition civetweb.c:18822
#define free
Definition civetweb.c:1578
const_iterator begin() const
const_iterator end() const
const clang::RecordDecl * GetRecordDecl() const
AnnotatedRecordDecl(long index, const clang::RecordDecl *decl, bool rStreamerInfo, bool rNoStreamer, bool rRequestNoInputOperator, bool rRequestOnlyTClass, int rRequestedVersionNumber, int rRequestedRNTupleSerializationMode, const std::string &rRequestedRNTupleSoARecord, const cling::Interpreter &interpret, const TNormalizedCtxt &normCtxt)
There is no requested type name.
const clang::RecordDecl * fDecl
const std::string & RequestedRNTupleSoARecord() const
const char * GetRequestedName() const
static std::string BuildDemangledTypeInfo(const clang::RecordDecl *rDecl, const std::string &normalizedName)
const std::string & GetDemangledTypeInfo() const
const char * GetNormalizedName() const
const clang::CXXRecordDecl * fArgType
const clang::CXXRecordDecl * GetType() const
RConstructorType(const char *type_of_arg, const cling::Interpreter &)
bool IsDeclaredScope(const std::string &base, bool &isInlined) override
bool IsAlreadyPartiallyDesugaredName(const std::string &nondef, const std::string &nameLong) override
TClingLookupHelper(cling::Interpreter &interpreter, TNormalizedCtxt &normCtxt, ExistingTypeCheck_t existingTypeCheck, CheckInClassTable_t CheckInClassTable, AutoParse_t autoParse, bool *shuttingDownPtr, const int *pgDebug=nullptr)
ExistingTypeCheck_t fExistingTypeCheck
CheckInClassTable_t fCheckInClassTable
bool GetPartiallyDesugaredNameWithScopeHandling(const std::string &tname, std::string &result, bool dropstd=true) override
We assume that we have a simple type: [const] typename[*&][const].
bool CheckInClassTable(const std::string &tname, std::string &result) override
void GetPartiallyDesugaredName(std::string &nameLong) override
bool ExistingTypeCheck(const std::string &tname, std::string &result) override
Helper routine to ry hard to avoid looking up in the Cling database as this could enduce an unwanted ...
const Config_t & GetConfig() const
TNormalizedCtxt::Config_t::SkipCollection DeclsCont_t
TNormalizedCtxt::TemplPtrIntMap_t TemplPtrIntMap_t
int GetNargsToKeep(const clang::ClassTemplateDecl *templ) const
Get from the map the number of arguments to keep.
TNormalizedCtxt::Config_t Config_t
TNormalizedCtxtImpl(const cling::LookupHelper &lh)
Initialize the list of typedef to keep (i.e.
TNormalizedCtxt::TypesCont_t TypesCont_t
void AddTemplAndNargsToKeep(const clang::ClassTemplateDecl *templ, unsigned int i)
Add to the internal map the pointer of a template as key and the number of template arguments to keep...
void keepTypedef(const cling::LookupHelper &lh, const char *name, bool replace=false)
Insert the type with name into the collection of typedefs to keep.
const TypesCont_t & GetTypeWithAlternative() const
const TemplPtrIntMap_t GetTemplNargsToKeepMap() const
static TemplPtrIntMap_t fTemplatePtrArgsToKeepMap
void AddTemplAndNargsToKeep(const clang::ClassTemplateDecl *templ, unsigned int i)
void keepTypedef(const cling::LookupHelper &lh, const char *name, bool replace=false)
cling::utils::Transform::Config Config_t
std::map< const clang::ClassTemplateDecl *, int > TemplPtrIntMap_t
TNormalizedCtxt(const cling::LookupHelper &lh)
TNormalizedCtxtImpl * fImpl
const TypesCont_t & GetTypeWithAlternative() const
std::set< const clang::Type * > TypesCont_t
int GetNargsToKeep(const clang::ClassTemplateDecl *templ) const
const Config_t & GetConfig() const
const TemplPtrIntMap_t GetTemplNargsToKeepMap() const
A RAII helper to remove and readd enclosing _Atomic() It expects no spaces at the beginning or end of...
Definition TClassEdit.h:160
#define I(x, y, z)
const std::string & GetPathSeparator()
int EncloseInNamespaces(const clang::Decl &decl, std::string &defString)
Take the namespaces which enclose the decl and put them around the definition string.
int FwdDeclFromTypeDefNameDecl(const clang::TypedefNameDecl &tdnDecl, const cling::Interpreter &interpreter, std::string &fwdDeclString, std::unordered_set< std::string > *fwdDeclSet=nullptr)
Extract "forward declaration" of a typedef.
int PrepareArgsForFwdDecl(std::string &templateArgs, const clang::TemplateParameterList &tmplParamList, const cling::Interpreter &interpreter)
Loop over the template parameters and build a string for template arguments using the fully qualified...
int FwdDeclFromTmplDecl(const clang::TemplateDecl &tmplDecl, const cling::Interpreter &interpreter, std::string &defString)
Convert a tmplt decl to its fwd decl.
const clang::RecordDecl * EncloseInScopes(const clang::Decl &decl, std::string &defString)
Take the scopes which enclose the decl and put them around the definition string.
int FwdDeclFromRcdDecl(const clang::RecordDecl &recordDecl, const cling::Interpreter &interpreter, std::string &defString, bool acceptStl=false)
Convert a rcd decl to its fwd decl If this is a template specialisation, treat in the proper way.
int GetDefArg(const clang::ParmVarDecl &par, std::string &valAsString, const clang::PrintingPolicy &pp)
Get the default value as string.
int FwdDeclIfTmplSpec(const clang::RecordDecl &recordDecl, const cling::Interpreter &interpreter, std::string &defString, const std::string &normalizedName)
Convert a tmplt decl to its fwd decl.
const std::string Decls2FwdDecls(const std::vector< const clang::Decl * > &decls, bool(*ignoreFiles)(const clang::PresumedLoc &), const cling::Interpreter &interp, std::string *logs)
bool HasClassDefMacro(const clang::Decl *decl, const cling::Interpreter &interpreter)
Return true if class has any of class declarations like ClassDef, ClassDefNV, ClassDefOverride.
llvm::StringRef GetClassComment(const clang::CXXRecordDecl &decl, clang::SourceLocation *loc, const cling::Interpreter &interpreter)
Return the class comment after the ClassDef: class MyClass { ... ClassDef(MyClass,...
const T * GetAnnotatedRedeclarable(const T *Redecl)
int extractPropertyNameValFromString(const std::string attributeStr, std::string &attrName, std::string &attrValue)
bool hasOpaqueTypedef(clang::QualType instanceType, const TNormalizedCtxt &normCtxt)
Return true if the type is a Double32_t or Float16_t or is a instance template that depends on Double...
EIOCtorCategory CheckConstructor(const clang::CXXRecordDecl *, const RConstructorType &, const cling::Interpreter &interp)
Check if class has constructor of provided type - either default or with single argument.
clang::RecordDecl * GetUnderlyingRecordDecl(clang::QualType type)
bool BeginsWith(const std::string &theString, const std::string &theSubstring)
bool IsDeclReacheable(const clang::Decl &decl)
Return true if the decl is representing an entity reacheable from the global namespace.
const clang::FunctionDecl * ClassInfo__HasMethod(const clang::DeclContext *cl, char const *, const cling::Interpreter &interp)
bool GetNameWithinNamespace(std::string &, std::string &, std::string &, clang::CXXRecordDecl const *)
Return true if one of the class' enclosing scope is a namespace and set fullname to the fully qualifi...
const clang::RecordDecl * ExtractEnclosingScopes(const clang::Decl &decl, std::list< std::pair< std::string, unsigned int > > &enclosingSc)
Extract the names and types of containing scopes.
bool HasCustomOperatorNewArrayPlacement(clang::RecordDecl const &, const cling::Interpreter &interp)
return true if we can find a custom operator new with placement
void Error(const char *location, const char *fmt,...)
void WriteClassInit(std::ostream &finalString, const AnnotatedRecordDecl &cl, const clang::CXXRecordDecl *decl, const cling::Interpreter &interp, const TNormalizedCtxt &normCtxt, const RConstructorTypes &ctorTypes, bool &needCollectionProxy)
FIXME: a function of 450+ lines!
void Info(const char *location, const char *fmt,...)
int WriteNamespaceHeader(std::ostream &, const clang::RecordDecl *)
int GetClassVersion(const clang::RecordDecl *cl, const cling::Interpreter &interp)
Return the version number of the class or -1 if the function Class_Version does not exist.
clang::QualType GetTypeForIO(const clang::QualType &templateInstanceType, const cling::Interpreter &interpreter, const TNormalizedCtxt &normCtxt, TClassEdit::EModType mode=TClassEdit::kNone)
int extractAttrString(clang::Attr *attribute, std::string &attrString)
Extract attr string.
void GetNormalizedName(std::string &norm_name, const clang::QualType &type, const cling::Interpreter &interpreter, const TNormalizedCtxt &normCtxt)
Return the type name normalized for ROOT, keeping only the ROOT opaque typedef (Double32_t,...
void WritePointersSTL(const AnnotatedRecordDecl &cl, const cling::Interpreter &interp, const TNormalizedCtxt &normCtxt)
Write interface function for STL members.
bool HasNewMerge(clang::CXXRecordDecl const *, const cling::Interpreter &)
Return true if the class has a method Merge(TCollection*,TFileMergeInfo*)
bool CheckPublicFuncWithProto(clang::CXXRecordDecl const *, char const *, char const *, const cling::Interpreter &, bool diagnose)
Return true, if the function (defined by the name and prototype) exists and is public.
std::string GetFileName(const clang::Decl &decl, const cling::Interpreter &interp)
Return the header file to be included to declare the Decl.
void WriteStandaloneReadRules(std::ostream &finalString, bool rawrules, std::vector< std::string > &standaloneTargets, const cling::Interpreter &interp)
clang::ClassTemplateDecl * QualType2ClassTemplateDecl(const clang::QualType &qt)
Extract from a qualtype the class template if this makes sense.
int IsSTLContainer(const AnnotatedRecordDecl &annotated)
Is this an STL container.
void Fatal(const char *location, const char *fmt,...)
std::list< RConstructorType > RConstructorTypes
int extractPropertyNameVal(clang::Attr *attribute, std::string &attrName, std::string &attrValue)
std::string GetModuleFileName(const char *moduleName)
Return the dictionary file name for a module.
clang::QualType ReSubstTemplateArg(clang::QualType input, const clang::Type *instance)
Check if 'input' or any of its template parameter was substituted when instantiating the class templa...
bool NeedDestructor(clang::CXXRecordDecl const *, const cling::Interpreter &)
bool EndsWith(const std::string &theString, const std::string &theSubstring)
void GetFullyQualifiedTypeName(std::string &name, const clang::QualType &type, const cling::Interpreter &interpreter)
bool NeedTemplateKeyword(clang::CXXRecordDecl const *)
bool HasCustomConvStreamerMemberFunction(const AnnotatedRecordDecl &cl, const clang::CXXRecordDecl *clxx, const cling::Interpreter &interp, const TNormalizedCtxt &normCtxt)
Return true if the class has a custom member function streamer.
bool HasDirectoryAutoAdd(clang::CXXRecordDecl const *, const cling::Interpreter &)
Return true if the class has a method DirectoryAutoAdd(TDirectory *)
const clang::FunctionDecl * GetFuncWithProto(const clang::Decl *cinfo, const char *method, const char *proto, const cling::Interpreter &gInterp, bool diagnose)
int ElementStreamer(std::ostream &finalString, const clang::NamedDecl &forcontext, const clang::QualType &qti, const char *t, int rwmode, const cling::Interpreter &interp, const char *tcl=nullptr)
bool MatchWithDeclOrAnyOfPrevious(const clang::CXXRecordDecl &cl, const clang::CXXRecordDecl &currentCl)
This is a recursive function.
clang::QualType GetNormalizedType(const clang::QualType &type, const cling::Interpreter &interpreter, const TNormalizedCtxt &normCtxt)
Return the type normalized for ROOT, keeping only the ROOT opaque typedef (Double32_t,...
const char * ShortTypeName(const char *typeDesc)
Return the absolute type of typeDesc.
void GetCppName(std::string &output, const char *input)
Return (in the argument 'output') a valid name of the C++ symbol/type (pass as 'input') that can be u...
bool HasCustomOperatorNewPlacement(char const *, clang::RecordDecl const &, const cling::Interpreter &)
return true if we can find a custom operator new with placement
bool IsStdClass(const clang::RecordDecl &cl)
Return true, if the decl is part of the std namespace.
bool HasResetAfterMerge(clang::CXXRecordDecl const *, const cling::Interpreter &)
Return true if the class has a method ResetAfterMerge(TFileMergeInfo *)
ROOT::ESTLType STLKind(const llvm::StringRef type)
Converts STL container name to number. vector -> 1, etc..
void WriteClassCode(CallWriteStreamer_t WriteStreamerFunc, const AnnotatedRecordDecl &cl, const cling::Interpreter &interp, const TNormalizedCtxt &normCtxt, std::ostream &finalString, const RConstructorTypes &ctorTypes, bool isGenreflex)
Generate the code of the class If the requestor is genreflex, request the new streamer format.
bool HasOldMerge(clang::CXXRecordDecl const *, const cling::Interpreter &)
Return true if the class has a method Merge(TCollection*)
int RemoveTemplateArgsFromName(std::string &name, unsigned int)
Remove the last n template arguments from the name.
long GetLineNumber(clang::Decl const *)
It looks like the template specialization decl actually contains less information on the location of ...
void WriteAuxFunctions(std::ostream &finalString, const AnnotatedRecordDecl &cl, const clang::CXXRecordDecl *decl, const cling::Interpreter &interp, const RConstructorTypes &ctorTypes, const TNormalizedCtxt &normCtxt)
std::string NormalizedName; GetNormalizedName(NormalizedName, decl->getASTContext()....
void foreachHeaderInModule(const clang::Module &module, const std::function< void(const clang::Module::Header &)> &closure, bool includeDirectlyUsedModules=true)
Calls the given lambda on every header in the given module.
bool IsBase(const clang::CXXRecordDecl *cl, const clang::CXXRecordDecl *base, const clang::CXXRecordDecl *context, const cling::Interpreter &interp)
void ExtractCtxtEnclosingNameSpaces(const clang::DeclContext &, std::list< std::pair< std::string, bool > > &)
Extract enclosing namespaces recursively.
std::pair< bool, int > GetTrivialIntegralReturnValue(const clang::FunctionDecl *funcCV, const cling::Interpreter &interp)
If the function contains 'just': return SomeValue; this routine will extract this value and return it...
bool HasCustomStreamerMemberFunction(const AnnotatedRecordDecl &cl, const clang::CXXRecordDecl *clxx, const cling::Interpreter &interp, const TNormalizedCtxt &normCtxt)
Return true if the class has a custom member function streamer.
std::string GetRealPath(const std::string &path)
clang::QualType AddDefaultParameters(clang::QualType instanceType, const cling::Interpreter &interpret, const TNormalizedCtxt &normCtxt)
Add any unspecified template parameters to the class template instance, mentioned anywhere in the typ...
std::pair< std::string, clang::QualType > GetNameTypeForIO(const clang::QualType &templateInstanceType, const cling::Interpreter &interpreter, const TNormalizedCtxt &normCtxt, TClassEdit::EModType mode=TClassEdit::kNone)
void GetQualifiedName(std::string &qual_name, const clang::QualType &type, const clang::NamedDecl &forcontext)
Main implementation relying on GetFullyQualifiedTypeName All other GetQualifiedName functions leverag...
bool ExtractAttrIntPropertyFromName(const clang::Decl &decl, const std::string &propName, int &propValue)
This routine counts on the "propName<separator>propValue" format.
bool IsLinkdefFile(const char *filename)
void WriteRulesRegistration(std::ostream &finalString, const std::string &dictName, const std::vector< std::string > &standaloneTargets)
llvm::StringRef GetComment(const clang::Decl &decl, clang::SourceLocation *loc=nullptr)
Returns the comment (// striped away), annotating declaration in a meaningful for ROOT IO way.
void SetPathsForRelocatability(std::vector< std::string > &clingArgs)
Organise the parameters for cling in order to guarantee relocatability It treats the gcc toolchain an...
bool IsStreamableObject(const clang::FieldDecl &m, const cling::Interpreter &interp)
void ReplaceAll(std::string &str, const std::string &from, const std::string &to, bool recurse=false)
bool QualType2Template(const clang::QualType &qt, clang::ClassTemplateDecl *&ctd, clang::ClassTemplateSpecializationDecl *&ctsd)
Get the template specialisation decl and template decl behind the qualtype Returns true if successful...
std::string TrueName(const clang::FieldDecl &m)
TrueName strips the typedefs and array dimensions.
const clang::Type * GetUnderlyingType(clang::QualType type)
Return the base/underlying type of a chain of array or pointers type.
ROOT::ESTLType IsSTLCont(const clang::RecordDecl &cl)
type : type name: vector<list<classA,allocator>,allocator> result: 0 : not stl container abs(result):...
bool IsStdDropDefaultClass(const clang::RecordDecl &cl)
Return true, if the decl is part of the std namespace and we want its default parameter dropped.
void ExtractTemplateNameFromQualType(const clang::QualType &qt, clang::TemplateName &theTemplateName, clang::ElaboratedTypeKeyword &theKeyword)
These manipulations are necessary because a template specialisation type does not inherit from a reco...
bool RequireCompleteType(const cling::Interpreter &interp, const clang::CXXRecordDecl *cl)
bool IsHeaderName(const std::string &filename)
void Warning(const char *location, const char *fmt,...)
bool IsCtxtReacheable(const clang::DeclContext &ctxt)
Return true if the DeclContext is representing an entity reacheable from the global namespace.
bool IsOfType(const clang::CXXRecordDecl &cl, const std::string &type, const cling::LookupHelper &lh)
const std::string & GetPathSeparator()
Return the separator suitable for this platform.
bool CheckDefaultConstructor(const clang::CXXRecordDecl *, const cling::Interpreter &interp)
Checks if default constructor exists and accessible.
EIOCtorCategory CheckIOConstructor(const clang::CXXRecordDecl *, const char *, const clang::CXXRecordDecl *, const cling::Interpreter &interp)
Checks IO constructor - must be public and with specified argument.
bool ExtractAttrPropertyFromName(const clang::Decl &decl, const std::string &propName, std::string &propValue)
This routine counts on the "propName<separator>propValue" format.
const clang::CXXRecordDecl * ScopeSearch(const char *name, const cling::Interpreter &gInterp, bool diagnose, const clang::Type **resultType)
Return the scope corresponding to 'name' or std::'name'.
int & GetErrorIgnoreLevel()
void ExtractEnclosingNameSpaces(const clang::Decl &, std::list< std::pair< std::string, bool > > &)
Extract the immediately outer namespace and then launch the recursion.
bool HasIOConstructor(clang::CXXRecordDecl const *, std::string &, const RConstructorTypes &, const cling::Interpreter &)
return true if we can find an constructor calleable without any arguments or with one the IOCtor spec...
llvm::StringRef DataMemberInfo__ValidArrayIndex(const cling::Interpreter &interp, const clang::DeclaratorDecl &m, int *errnum=nullptr, llvm::StringRef *errstr=nullptr)
ValidArrayIndex return a static string (so use it or copy it immediatly, do not call GrabIndex twice ...
void WriteSchemaList(std::list< SchemaRuleMap_t > &rules, const std::string &listName, std::ostream &output)
Write schema rules.
std::map< std::string, ROOT::Internal::TSchemaType > MembersTypeMap_t
ESTLType
Definition ESTLType.h:28
@ kSTLbitset
Definition ESTLType.h:37
@ kSTLmap
Definition ESTLType.h:33
@ kSTLunorderedmultiset
Definition ESTLType.h:43
@ kROOTRVec
Definition ESTLType.h:46
@ kSTLend
Definition ESTLType.h:47
@ kSTLset
Definition ESTLType.h:35
@ kSTLmultiset
Definition ESTLType.h:36
@ kSTLdeque
Definition ESTLType.h:32
@ kSTLvector
Definition ESTLType.h:30
@ kSTLunorderedmultimap
Definition ESTLType.h:45
@ kSTLunorderedset
Definition ESTLType.h:42
@ kSTLlist
Definition ESTLType.h:31
@ kSTLforwardlist
Definition ESTLType.h:41
@ kSTLunorderedmap
Definition ESTLType.h:44
@ kNotSTL
Definition ESTLType.h:29
@ kSTLmultimap
Definition ESTLType.h:34
void WriteReadRuleFunc(SchemaRuleMap_t &rule, int index, std::string &mappedName, MembersTypeMap_t &members, std::ostream &output)
Write the conversion function for Read rule, the function name is being written to rule["funcname"].
R__EXTERN SchemaRuleClassMap_t gReadRules
bool HasValidDataMembers(SchemaRuleMap_t &rule, MembersTypeMap_t &members, std::string &error_string)
Check if given rule contains references to valid data members.
void WriteReadRawRuleFunc(SchemaRuleMap_t &rule, int index, std::string &mappedName, MembersTypeMap_t &members, std::ostream &output)
Write the conversion function for ReadRaw rule, the function name is being written to rule["funcname"...
R__EXTERN SchemaRuleClassMap_t gReadRawRules
ROOT::ESTLType STLKind(std::string_view type)
Converts STL container name to number.
bool IsStdClass(const char *type)
return true if the class belongs to the std namespace
std::string GetLong64_Name(const char *original)
Replace 'long long' and 'unsigned long long' by 'Long64_t' and 'ULong64_t'.
ROOT::ESTLType IsSTLCont(std::string_view type)
type : type name: vector<list<classA,allocator>,allocator> result: 0 : not stl container code of cont...
char * DemangleName(const char *mangled_name, int &errorCode)
Definition TClassEdit.h:255
std::string GetNameForIO(const std::string &templateInstanceName, TClassEdit::EModType mode=TClassEdit::kNone, bool *hasChanged=nullptr)
void GetNormalizedName(std::string &norm_name, std::string_view name)
Return the normalized name.
@ kKeepOuterConst
Definition TClassEdit.h:88
@ kDropStlDefault
Definition TClassEdit.h:83
bool IsSTLBitset(const char *type)
Return true is the name is std::bitset<number> or bitset<number>
constexpr Double_t C()
Velocity of light in .
Definition TMath.h:117
static const char * what
Definition stlLoader.cc:5
TMarker m
Definition textangle.C:8
auto * tt
Definition textangle.C:16