Revision 1cb045683bcdd985c49315bbd73eba9d9c7d8969 authored by Helge Voss on 06 April 2016, 16:35:19 UTC, committed by Helge Voss on 06 April 2016, 16:40:36 UTC
1 parent 0c935d4
Scanner.cxx
// @(#)root/utils/src:$Id$
// Author: Philippe Canal November 2011 ; originated from Zdenek Culik 16/04/2010 and Velislava Spasova.
/*************************************************************************
* Copyright (C) 1995-2011, Rene Brun and Fons Rademakers. *
* All rights reserved. *
* *
* For the licensing terms see $ROOTSYS/LICENSE. *
* For the list of contributors see $ROOTSYS/README/rootcint. *
*************************************************************************/
#include "Scanner.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/SmallSet.h"
#include "clang/Sema/Sema.h"
#include "clang/Frontend/CompilerInstance.h"
#include "cling/Interpreter/Interpreter.h"
#include "llvm/Support/Path.h"
#include "TClassEdit.h"
#include <iostream>
#include <sstream> // class ostringstream
#include "SelectionRules.h"
//#define DEBUG
#define SHOW_WARNINGS
// #define SHOW_TEMPLATE_INFO
// #define COMPLETE_TEMPLATES
// #define CHECK_TYPES
#define FILTER_WARNINGS
#define DIRECT_OUTPUT
// SHOW_WARNINGS - enable warnings
// SHOW_TEMPLATE_INFO - enable informations about encoutered tempaltes
// COMPLETE_TEMPLATES - process templates, not only specializations (instantiations)
// FILTER_WARNINGS -- do not repeat same type of warning
// DIRECT_OUTPUT -- output to std err with gcc compatible filename an line number
// #define SELECTION_DEBUG
namespace {
class RPredicateIsSameNamespace
{
private:
clang::NamespaceDecl *fTarget;
public:
RPredicateIsSameNamespace(clang::NamespaceDecl *target) : fTarget(target) {}
bool operator()(const RScanner::AnnotatedNamespaceDecl& element)
{
return (fTarget == element);
}
};
template<class T>
inline static bool IsElementPresent(const std::vector<T> &v, const T &el){
return std::find(v.begin(),v.end(),el) != v.end();
}
}
using namespace ROOT;
using namespace clang;
extern cling::Interpreter *gInterp;
const char* RScanner::fgClangDeclKey = "ClangDecl"; // property key used for connection with Clang objects
const char* RScanner::fgClangFuncKey = "ClangFunc"; // property key for demangled names
int RScanner::fgAnonymousClassCounter = 0;
int RScanner::fgBadClassCounter = 0;
int RScanner::fgAnonymousEnumCounter = 0;
std::map <clang::Decl*, std::string> RScanner::fgAnonymousClassMap;
std::map <clang::Decl*, std::string> RScanner::fgAnonymousEnumMap;
////////////////////////////////////////////////////////////////////////////////
/// Regular constructor setting up the scanner to search for entities
/// matching the 'rules'.
RScanner::RScanner (SelectionRules &rules,
EScanType stype,
const cling::Interpreter &interpret,
ROOT::TMetaUtils::TNormalizedCtxt &normCtxt,
unsigned int verbose /* = 0 */) :
fVerboseLevel(verbose),
fSourceManager(0),
fInterpreter(interpret),
fRecordDeclCallback(0),
fNormCtxt(normCtxt),
fSelectionRules(rules),
fScanType(stype),
fFirstPass(true)
{
// Build the cache for all selection rules
fSelectionRules.FillCache();
for (int i = 0; i <= fgDeclLast; i ++)
fDeclTable [i] = false;
for (int i = 0; i <= fgTypeLast; i ++)
fTypeTable [i] = false;
fLastDecl = 0;
}
////////////////////////////////////////////////////////////////////////////////
RScanner::~RScanner ()
{
}
////////////////////////////////////////////////////////////////////////////////
inline void* ToDeclProp(clang::Decl* item)
{
/* conversion and type check used by AddProperty */
return item;
}
////////////////////////////////////////////////////////////////////////////////
inline size_t APIntToSize(const llvm::APInt& num)
{
return *num.getRawData();
}
////////////////////////////////////////////////////////////////////////////////
inline long APIntToLong(const llvm::APInt& num)
{
return *num.getRawData();
}
////////////////////////////////////////////////////////////////////////////////
inline std::string APIntToStr(const llvm::APInt& num)
{
return num.toString(10, true);
}
////////////////////////////////////////////////////////////////////////////////
inline std::string IntToStr(int num)
{
std::string txt = "";
txt += num;
return txt;
}
////////////////////////////////////////////////////////////////////////////////
inline std::string IntToStd(int num)
{
std::ostringstream stream;
stream << num;
return stream.str();
}
////////////////////////////////////////////////////////////////////////////////
inline std::string Message(const std::string &msg, const std::string &location)
{
std::string loc = location;
#ifdef DIRECT_OUTPUT
int n = loc.length ();
while (n > 0 && loc [n] != ':')
n--;
if (n > 0)
loc = loc.substr (0, n) + ":";
#endif
if (loc == "")
return msg;
else
return loc + " " + msg;
}
////////////////////////////////////////////////////////////////////////////////
void RScanner::ShowInfo(const std::string &msg, const std::string &location) const
{
const std::string message = Message(msg, location);
#ifdef DIRECT_OUTPUT
std::cout << message << std::endl;
#else
fReporter->Info("RScanner:ShowInfo", "CLR %s", message.Data());
#endif
}
////////////////////////////////////////////////////////////////////////////////
void RScanner::ShowWarning(const std::string &msg, const std::string &location) const
{
#ifdef SHOW_WARNINGS
const std::string message = Message(msg, location);
#ifdef DIRECT_OUTPUT
std::cout << message << std::endl;
#else
fReporter->Warning("RScanner:ShowWarning", "CLR %s", message.Data());
#endif
#endif
}
////////////////////////////////////////////////////////////////////////////////
void RScanner::ShowError(const std::string &msg, const std::string &location) const
{
const std::string message = Message(msg, location);
#ifdef DIRECT_OUTPUT
std::cout << message << std::endl;
#else
fReporter->Error("RScanner:ShowError", "CLR %s", message.Data());
#endif
}
////////////////////////////////////////////////////////////////////////////////
void RScanner::ShowTemplateInfo(const std::string &msg, const std::string &location) const
{
#ifdef SHOW_TEMPLATE_INFO
std::string loc = location;
if (loc == "")
loc = GetLocation (fLastDecl);
ShowWarning(msg, loc);
#endif
}
////////////////////////////////////////////////////////////////////////////////
std::string RScanner::GetSrcLocation(clang::SourceLocation L) const
{
std::string location = "";
llvm::raw_string_ostream stream(location);
L.print(stream, *fSourceManager);
return stream.str();
}
////////////////////////////////////////////////////////////////////////////////
std::string RScanner::GetLocation(clang::Decl* D) const
{
if (D == NULL)
{
return "";
}
else
{
std::string location = "";
llvm::raw_string_ostream stream(location);
D->getLocation().print(stream, *fSourceManager);
return stream.str();
}
}
////////////////////////////////////////////////////////////////////////////////
std::string RScanner::GetName(clang::Decl* D) const
{
std::string name = "";
// std::string kind = D->getDeclKindName();
if (clang::NamedDecl* ND = dyn_cast <clang::NamedDecl> (D)) {
name = ND->getQualifiedNameAsString();
}
return name;
}
////////////////////////////////////////////////////////////////////////////////
inline std::string AddSpace(const std::string &txt)
{
if (txt == "")
return "";
else
return txt + " ";
}
////////////////////////////////////////////////////////////////////////////////
void RScanner::DeclInfo(clang::Decl* D) const
{
std::string location = GetLocation(D);
std::string kind = D->getDeclKindName();
std::string name = GetName(D);
ShowInfo("Scan: " + kind + " declaration " + name, location);
}
////////////////////////////////////////////////////////////////////////////////
/// unknown - this kind of declaration was not known to programmer
void RScanner::UnknownDecl(clang::Decl* D, const std::string &txt) const
{
std::string location = GetLocation(D);
std::string kind = D->getDeclKindName();
std::string name = GetName(D);
ShowWarning("Unknown " + AddSpace(txt) + kind + " declaration " + name, location);
}
////////////////////////////////////////////////////////////////////////////////
/// unexpected - this kind of declaration is unexpected (in concrete place)
void RScanner::UnexpectedDecl(clang::Decl* D, const std::string &txt) const
{
std::string location = GetLocation(D);
std::string kind = D->getDeclKindName();
std::string name = GetName(D);
ShowWarning("Unexpected " + kind + " declaration " + name, location);
}
////////////////////////////////////////////////////////////////////////////////
/// unsupported - this kind of declaration is probably not used (in current version of C++)
void RScanner::UnsupportedDecl(clang::Decl* D, const std::string &txt) const
{
std::string location = GetLocation(D);
std::string kind = D->getDeclKindName();
std::string name = GetName(D);
ShowWarning("Unsupported " + AddSpace(txt) + kind + " declaration " + name, location);
}
////////////////////////////////////////////////////////////////////////////////
/// unimportant - this kind of declaration is not stored into reflex
void RScanner::UnimportantDecl(clang::Decl* D, const std::string &txt) const
{
}
////////////////////////////////////////////////////////////////////////////////
/// information about item, that should be implemented
void RScanner::UnimplementedDecl(clang::Decl* D, const std::string &txt)
{
clang::Decl::Kind k = D->getKind();
bool show = true;
#ifdef FILTER_WARNINGS
if (k >= 0 || k <= fgDeclLast) {
if (fDeclTable [k])
show = false; // already displayed
else
fDeclTable [k] = true;
}
#endif
if (show)
{
std::string location = GetLocation(D);
std::string kind = D->getDeclKindName();
std::string name = GetName(D);
std::string msg = "Unimplemented ";
if (txt == "") {
msg += "declaration";
} else {
msg += txt;
}
msg += ": ";
msg += kind;
msg += " ";
msg += name;
ShowWarning(msg,location);
}
}
////////////////////////////////////////////////////////////////////////////////
void RScanner::UnknownType(clang::QualType qual_type) const
{
std::string location = GetLocation(fLastDecl);
std::string kind = qual_type.getTypePtr()->getTypeClassName();
ShowWarning("Unknown " + kind + " type " + qual_type.getAsString(), location);
}
////////////////////////////////////////////////////////////////////////////////
void RScanner::UnsupportedType(clang::QualType qual_type) const
{
std::string location = GetLocation(fLastDecl);
std::string kind = qual_type.getTypePtr()->getTypeClassName();
ShowWarning("Unsupported " + kind + " type " + qual_type.getAsString(), location);
}
////////////////////////////////////////////////////////////////////////////////
/// unimportant - this kind of declaration is not stored into reflex
void RScanner::UnimportantType(clang::QualType qual_type) const
{
}
////////////////////////////////////////////////////////////////////////////////
void RScanner::UnimplementedType(clang::QualType qual_type)
{
clang::Type::TypeClass k = qual_type.getTypePtr()->getTypeClass();
bool show = true;
#ifdef FILTER_WARNINGS
if (k >= 0 || k <= fgTypeLast) {
if (fTypeTable [k])
show = false; // already displayed
else
fTypeTable [k] = true;
}
#endif
if (show)
{
std::string location = GetLocation(fLastDecl);
std::string kind = qual_type.getTypePtr()->getTypeClassName();
ShowWarning("Unimplemented type: " + kind + " " + qual_type.getAsString(), location);
}
}
////////////////////////////////////////////////////////////////////////////////
void RScanner::UnimplementedType (const clang::Type* T)
{
clang::Type::TypeClass k = T->getTypeClass();
bool show = true;
#ifdef FILTER_WARNINGS
if (k >= 0 || k <= fgTypeLast) {
if (fTypeTable [k])
show = false; // already displayed
else
fTypeTable [k] = true;
}
#endif
if (show)
{
std::string location = GetLocation(fLastDecl);
std::string kind = T->getTypeClassName ();
ShowWarning ("Unimplemented type: " + kind, location);
}
}
////////////////////////////////////////////////////////////////////////////////
std::string RScanner::GetClassName(clang::RecordDecl* D) const
{
std::string cls_name = D->getQualifiedNameAsString();
// NO if (cls_name == "")
// NO if (D->isAnonymousStructOrUnion())
// NO if (cls_name == "(anonymous)") {
if (! D->getDeclName ()) {
if (fgAnonymousClassMap.find (D) != fgAnonymousClassMap.end())
{
// already encountered anonymous class
cls_name = fgAnonymousClassMap [D];
}
else
{
fgAnonymousClassCounter ++;
cls_name = "_ANONYMOUS_CLASS_" + IntToStd(fgAnonymousClassCounter) + "_"; // !?
fgAnonymousClassMap [D] = cls_name;
// ShowInfo ("anonymous class " + cls_name, GetLocation (D));
}
}
return cls_name;
}
////////////////////////////////////////////////////////////////////////////////
std::string RScanner::GetEnumName(clang::EnumDecl* D) const
{
std::string enum_name = D->getQualifiedNameAsString();
if (! D->getDeclName ()) {
if (fgAnonymousEnumMap.find (D) != fgAnonymousEnumMap.end())
{
// already encountered anonymous enumeration type
enum_name = fgAnonymousEnumMap [D];
}
else
{
fgAnonymousEnumCounter ++;
enum_name = "_ANONYMOUS_ENUM_" + IntToStd(fgAnonymousEnumCounter) + "_"; // !?
fgAnonymousEnumMap [D] = enum_name;
// ShowInfo ("anonymous enum " + enum_name, GetLocation (D));
}
}
return enum_name;
}
////////////////////////////////////////////////////////////////////////////////
std::string RScanner::ExprToStr(clang::Expr* expr) const
{
clang::LangOptions lang_opts;
clang::PrintingPolicy print_opts(lang_opts); // !?
std::string text = "";
llvm::raw_string_ostream stream(text);
expr->printPretty(stream, NULL, print_opts);
return stream.str();
}
////////////////////////////////////////////////////////////////////////////////
std::string RScanner::ConvTemplateName(clang::TemplateName& N) const
{
clang::LangOptions lang_opts;
clang::PrintingPolicy print_opts(lang_opts); // !?
std::string text = "";
llvm::raw_string_ostream stream(text);
N.print(stream, print_opts);
return stream.str();
}
#ifdef COMPLETE_TEMPLATES
////////////////////////////////////////////////////////////////////////////////
std::string RScanner::ConvTemplateParameterList(clang::TemplateParameterList* list) const
{
std::string result = "";
bool any = false;
for (clang::TemplateParameterList::iterator I = list->begin(), E = list->end(); I != E; ++I) {
if (any)
result += ",";
any = true;
clang::NamedDecl * D = *I;
switch (D->getKind()) {
case clang::Decl::TemplateTemplateParm:
UnimplementedDecl(dyn_cast <clang::TemplateTemplateParmDecl> (D), "template parameter");
break;
case clang::Decl::TemplateTypeParm:
{
clang::TemplateTypeParmDecl* P = dyn_cast <clang::TemplateTypeParmDecl> (D);
if (P->wasDeclaredWithTypename())
result += "typename ";
else
result += "class ";
if (P->isParameterPack())
result += "... ";
result += P->getNameAsString();
}
break;
case clang::Decl::NonTypeTemplateParm:
{
clang::NonTypeTemplateParmDecl* P = dyn_cast <clang::NonTypeTemplateParmDecl> (D);
result += P->getType().getAsString();
if (clang::IdentifierInfo* N = P->getIdentifier()) {
result += " ";
std::string s = N->getName();
result += s;
}
if (P->hasDefaultArgument())
result += " = " + ExprToStr(P->getDefaultArgument());
}
break;
default:
UnknownDecl(*I, "template parameter");
}
}
// ShowInfo ("template parameters <" + result + ">");
return "<" + result + ">";
}
////////////////////////////////////////////////////////////////////////////////
std::string RScanner::ConvTemplateParams(clang::TemplateDecl* D)
{
return ConvTemplateParameterList(D->getTemplateParameters());
}
////////////////////////////////////////////////////////////////////////////////
std::string RScanner::ConvTemplateArguments(const clang::TemplateArgumentList& list)
{
clang::LangOptions lang_opts;
clang::PrintingPolicy print_opts(lang_opts); // !?
return clang::TemplateSpecializationType::PrintTemplateArgumentList
(list.data(), list.size(), print_opts);
}
#endif // COMPLETE_TEMPLATES
////////////////////////////////////////////////////////////////////////////////
std::string RScanner::FuncParameters(clang::FunctionDecl* D) const
{
std::string result = "";
for (clang::FunctionDecl::param_iterator I = D->param_begin(), E = D->param_end(); I != E; ++I) {
clang::ParmVarDecl* P = *I;
if (result != "")
result += ";"; // semicolon, not comma, important
std::string type = P->getType().getAsString();
std::string name = P->getNameAsString();
result += type + " " + name;
// NO if (P->hasDefaultArg ()) // check hasUnparsedDefaultArg () and hasUninstantiatedDefaultArg ()
if (P->getInit()) {
std::string init_value = ExprToStr(P->getDefaultArg());
result += "=" + init_value;
}
}
return result;
}
////////////////////////////////////////////////////////////////////////////////
std::string RScanner::FuncParameterList(clang::FunctionDecl* D) const
{
std::string result = "";
for (clang::FunctionDecl::param_iterator I = D->param_begin(), E = D->param_end(); I != E; ++I) {
clang::ParmVarDecl* P = *I;
if (result != "")
result += ",";
std::string type = P->getType().getAsString();
result += type;
}
return "(" + result + ")";
}
////////////////////////////////////////////////////////////////////////////////
/// This method visits a namespace node
bool RScanner::VisitNamespaceDecl(clang::NamespaceDecl* N)
{
// We don't need to visit this while creating the big PCM
if (fScanType == EScanType::kOnePCM)
return true;
// in case it is implicit we don't create a builder
if(N && N->isImplicit()){
return true;
}
bool ret = true;
const ClassSelectionRule *selected = fSelectionRules.IsDeclSelected(N);
if (selected) {
#ifdef SELECTION_DEBUG
if (fVerboseLevel > 3) std::cout<<"\n\tSelected -> true";
#endif
clang::DeclContext* primary_ctxt = N->getPrimaryContext();
clang::NamespaceDecl* primary = llvm::dyn_cast<clang::NamespaceDecl>(primary_ctxt);
RPredicateIsSameNamespace pred(primary);
if ( find_if(fSelectedNamespaces.begin(),fSelectedNamespaces.end(),pred) == fSelectedNamespaces.end() ) {
// The namespace is not already registered.
if (fVerboseLevel > 0) {
std::string qual_name;
GetDeclQualName(N,qual_name);
// std::cout<<"\tSelected namespace -> " << qual_name << " ptr " << (void*)N << " decl ctxt " << (void*)N->getPrimaryContext() << " classname " <<primary->getNameAsString() << "\n";
std::cout<<"\tSelected namespace -> " << qual_name << "\n";
}
fSelectedNamespaces.push_back(AnnotatedNamespaceDecl(primary,selected->GetIndex(),selected->RequestOnlyTClass()));
}
ret = true;
}
else {
#ifdef SELECTION_DEBUG
if (fVerboseLevel > 3) std::cout<<"\n\tSelected -> false";
#endif
}
// DEBUG if(ret) std::cout<<"\n\tReturning true ...";
// DEBUG else std::cout<<"\n\tReturning false ...";
return ret;
}
////////////////////////////////////////////////////////////////////////////////
bool RScanner::VisitRecordDecl(clang::RecordDecl* D)
{
// This method visits a class node
return TreatRecordDeclOrTypedefNameDecl(D);
}
////////////////////////////////////////////////////////////////////////////////
bool RScanner::TreatRecordDeclOrTypedefNameDecl(clang::TypeDecl* typeDecl)
{
// For every class is created a new class buider irrespectful of weather the
// class is internal for another class declaration or not.
// RecordDecls and TypedefDecls (or RecordDecls!) are treated.
// We follow two different codepaths if the typeDecl is a RecordDecl or
// a TypedefDecl. If typeDecl is a TypedefDecl, recordDecl becomes the
// underlying RecordDecl.
// This is done to leverage the selections rule matching in SelectionRules
// which works basically with names.
// At the end of the method, if the typedef name is matched, an AnnotatedRecordDecl
// with the underlying RecordDecl is fed to the machinery.
clang::RecordDecl* recordDecl = clang::dyn_cast<clang::RecordDecl>(typeDecl);
clang::TypedefNameDecl* typedefNameDecl = clang::dyn_cast<clang::TypedefNameDecl>(typeDecl);
// If typeDecl is not a RecordDecl, try to fetch the RecordDecl behind the TypedefDecl
if (!recordDecl && typedefNameDecl) {
recordDecl = ROOT::TMetaUtils::GetUnderlyingRecordDecl(typedefNameDecl->getUnderlyingType());
}
// If at this point recordDecl is still NULL, we have a problem
if (!recordDecl) {
ROOT::TMetaUtils::Warning("RScanner::TreatRecordDeclOrTypeNameDecl",
"Could not cast typeDecl either to RecordDecl or could not get RecordDecl underneath typedef.\n");
return true;
}
// Do not select unnamed records.
if (!recordDecl->getIdentifier())
return true;
if (fScanType == EScanType::kOnePCM && ROOT::TMetaUtils::IsStdClass(*recordDecl))
return true;
// At this point, recordDecl must be a RecordDecl pointer.
if (recordDecl && fRecordDeclCallback) {
// Pass on any declaration. This is usually used to record dependency.
// Since rootcint see C++ compliant header files, we can assume that
// if a forward declaration or declaration has been inserted, the
// classes for which we are creating a dictionary will be using
// them either directly or indirectly. Any false positive can be
// resolved by removing the spurrious dependency in the (user) header
// files.
std::string qual_name;
GetDeclQualName(recordDecl,qual_name);
fRecordDeclCallback(qual_name.c_str());
}
// in case it is implicit or a forward declaration, we are not interested.
if(recordDecl && (recordDecl->isImplicit() || !recordDecl->isCompleteDefinition()) ) {
return true;
}
// Never select the class templates themselves.
const clang::CXXRecordDecl *cxxdecl = llvm::dyn_cast<clang::CXXRecordDecl>(recordDecl);
if (cxxdecl && cxxdecl->getDescribedClassTemplate ()) {
return true;
}
const ClassSelectionRule *selectedFromTypedef = typedefNameDecl ? fSelectionRules.IsDeclSelected(typedefNameDecl) : 0;
const ClassSelectionRule *selectedFromRecDecl = fSelectionRules.IsDeclSelected(recordDecl);
const ClassSelectionRule *selected = typedefNameDecl ? selectedFromTypedef : selectedFromRecDecl;
if (! selected) return true; // early exit. Nothing more to be done.
// Selected through typedef but excluded with concrete classname
bool excludedFromRecDecl = false;
if ( selectedFromRecDecl )
excludedFromRecDecl = selectedFromRecDecl->GetSelected() == BaseSelectionRule::kNo;
if (selected->GetSelected() == BaseSelectionRule::kYes && !excludedFromRecDecl) {
// The record decl will results to be selected
// Save the typedef
if (selectedFromTypedef){
if (!IsElementPresent(fSelectedTypedefs, typedefNameDecl))
fSelectedTypedefs.push_back(typedefNameDecl);
// Early exit here if we are not in presence of XML
if (!fSelectionRules.IsSelectionXMLFile()) return true;
}
if (fSelectionRules.IsSelectionXMLFile() && selected->IsFromTypedef()) {
if (!IsElementPresent(fSelectedTypedefs, typedefNameDecl))
fSelectedTypedefs.push_back(typedefNameDecl);
return true;
}
if (typedefNameDecl)
ROOT::TMetaUtils::Info("RScanner::TreatRecordDeclOrTypedefNameDecl",
"Typedef is selected %s.\n", typedefNameDecl->getNameAsString().c_str());
// For the case kNo, we could (but don't) remove the node from the pcm
// For the case kDontCare, the rule is just a place holder and we are actually trying to exclude some of its children
// (this is used only in the selection xml case).
// Reject the selection of std::pair on the ground that it is trivial
// and can easily be recreated from the AST information.
if (recordDecl && recordDecl->getName() == "pair") {
const clang::NamespaceDecl *nsDecl = llvm::dyn_cast<clang::NamespaceDecl>(recordDecl->getDeclContext());
if (!nsDecl){
ROOT::TMetaUtils::Error("RScanner::TreatRecordDeclOrTypedefNameDecl",
"Cannot convert context of RecordDecl called pair into a namespace.\n");
return true;
}
const clang::NamespaceDecl *nsCanonical = nsDecl->getCanonicalDecl();
if (nsCanonical && nsCanonical == fInterpreter.getCI()->getSema().getStdNamespace()) {
if (selected->HasAttributeFileName() || selected->HasAttributeFilePattern()) {
return true;
}
}
}
// Insert in the selected classes if not already there
// We need this check since the same class can be selected through its name or typedef
bool rcrdDeclNotAlreadySelected = fselectedRecordDecls.insert((RecordDecl*)recordDecl->getCanonicalDecl()).second;
auto declSelRuleMapIt = fDeclSelRuleMap.find(recordDecl->getCanonicalDecl());
if (!fFirstPass &&
!rcrdDeclNotAlreadySelected &&
declSelRuleMapIt != fDeclSelRuleMap.end() &&
declSelRuleMapIt->second != selected){
std::string normName;
TMetaUtils::GetNormalizedName(normName,
recordDecl->getASTContext().getTypeDeclType(recordDecl),
fInterpreter,
fNormCtxt);
auto previouslyMatchingRule = (const ClassSelectionRule*)declSelRuleMapIt->second;
int previouslineno = previouslyMatchingRule->GetLineNumber();
std::string cleanFileName = llvm::sys::path::filename(selected->GetSelFileName());
auto lineno = selected->GetLineNumber();
auto rulesAreCompatible = SelectionRulesUtils::areEqual<ClassSelectionRule>(selected, previouslyMatchingRule, true /*moduloNameOrPattern*/);
if (!rulesAreCompatible){
std::stringstream message;
if (lineno > 1) message << "Selection file " << cleanFileName << ", lines "
<< lineno << " and " << previouslineno << ". ";
message << "Attempt to select a class "<< normName << " with two rules which have incompatible attributes. "
<< "The attributes such as transiency might not be correctly propagated to the typesystem of ROOT.\n";
selected->Print(message);
message << "Conflicting rule already matched:\n";
previouslyMatchingRule->Print(message);
ROOT::TMetaUtils::Warning(0,"%s\n", message.str().c_str());
}
}
fDeclSelRuleMap[recordDecl->getCanonicalDecl()]=selected;
if(rcrdDeclNotAlreadySelected &&
!fFirstPass){
// Before adding the decl to the selected ones, check its access.
// We do not yet support I/O of private or protected classes.
// See ROOT-7450
// We exclude filename selections as they can come from aclic
auto isFileSelection = selected->HasAttributeFileName() &&
selected->HasAttributePattern() &&
"*" == selected->GetAttributePattern();
auto canDeclAccess = recordDecl->getCanonicalDecl()->getAccess();
if (!isFileSelection && (AS_protected == canDeclAccess || AS_private == canDeclAccess)){
std::string normName;
TMetaUtils::GetNormalizedName(normName,
recordDecl->getASTContext().getTypeDeclType(recordDecl),
fInterpreter,
fNormCtxt);
auto msg = "Class or struct %s was selected but its dictionary cannot be generated: "
"this is a private or protected class and this is not supported. No direct "
"I/O operation of %s instances will be possible.\n";
ROOT::TMetaUtils::Warning(0,msg,normName.c_str(),normName.c_str());
return true;
}
const std::string& name_value = selected->GetAttributeName();
if (selected->HasAttributeName()) {
ROOT::TMetaUtils::AnnotatedRecordDecl annRecDecl(selected->GetIndex(),
selected->GetRequestedType(),
recordDecl,
name_value.c_str(),
selected->RequestStreamerInfo(),
selected->RequestNoStreamer(),
selected->RequestNoInputOperator(),
selected->RequestOnlyTClass(),
selected->RequestedVersionNumber(),
fInterpreter,
fNormCtxt);
fSelectedClasses.push_back(annRecDecl);
} else {
ROOT::TMetaUtils::AnnotatedRecordDecl annRecDecl(selected->GetIndex(),
recordDecl,
selected->RequestStreamerInfo(),
selected->RequestNoStreamer(),
selected->RequestNoInputOperator(),
selected->RequestOnlyTClass(),
selected->RequestedVersionNumber(),
fInterpreter,
fNormCtxt);
fSelectedClasses.push_back(annRecDecl);
}
if (fVerboseLevel > 0) {
std::string qual_name;
GetDeclQualName(recordDecl,qual_name);
std::string normName;
TMetaUtils::GetNormalizedName(normName,
recordDecl->getASTContext().getTypeDeclType(recordDecl),
fInterpreter,
fNormCtxt);
std::string typedef_qual_name;
std::string typedefMsg;
if (typedefNameDecl){
GetDeclQualName(typedefNameDecl,typedef_qual_name);
typedefMsg = "(through typedef/alias " + typedef_qual_name + ") ";
}
std::cout <<"Selected class "
<< typedefMsg
<< "-> "
<< qual_name
<< " for ROOT: "
<< normName
<< "\n";
}
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// Visitor for every TypedefNameDecl, i.e. aliases and typedefs
/// We check three conditions before trying to match the name:
/// 1) If we are creating a big PCM
/// 2) If the underlying decl is a RecordDecl
/// 3) If the typedef is eventually contained in the std namespace
bool RScanner::VisitTypedefNameDecl(clang::TypedefNameDecl* D)
{
if (fScanType == EScanType::kOnePCM)
return true;
const clang::DeclContext *ctx = D->getDeclContext();
bool isInStd=false;
if (ctx) {
const clang::NamedDecl *parent = llvm::dyn_cast<clang::NamedDecl> (ctx);
isInStd = parent && 0 == parent->getQualifiedNameAsString().compare(0,5,"std::");
}
if (ROOT::TMetaUtils::GetUnderlyingRecordDecl(D->getUnderlyingType()) &&
!isInStd){
TreatRecordDeclOrTypedefNameDecl(D);
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool RScanner::VisitEnumDecl(clang::EnumDecl* D)
{
if (fScanType == EScanType::kOnePCM)
return true;
if(fSelectionRules.IsDeclSelected(D) &&
!IsElementPresent(fSelectedEnums, D)){ // Removal of duplicates.
fSelectedEnums.push_back(D);
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool RScanner::VisitVarDecl(clang::VarDecl* D)
{
if (!D->hasGlobalStorage() ||
fScanType == EScanType::kOnePCM)
return true;
if(fSelectionRules.IsDeclSelected(D)){
fSelectedVariables.push_back(D);
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// Nothing to be done here
bool RScanner::VisitFieldDecl(clang::FieldDecl* D)
{
return true;
// bool ret = true;
//
// if(fSelectionRules.IsDeclSelected(D)){
// #ifdef SELECTION_DEBUG
// if (fVerboseLevel > 3) std::cout<<"\n\tSelected -> true";
// #endif
//
// // if (fVerboseLevel > 0) {
// // std::string qual_name;
// // GetDeclQualName(D,qual_name);
// // std::cout<<"\tSelected field -> " << qual_name << "\n";
// // }
// }
// else {
// #ifdef SELECTION_DEBUG
// if (fVerboseLevel > 3) std::cout<<"\n\tSelected -> false";
// #endif
// }
//
// return ret;
}
////////////////////////////////////////////////////////////////////////////////
bool RScanner::VisitFunctionDecl(clang::FunctionDecl* D)
{
if (fScanType == EScanType::kOnePCM)
return true;
if(clang::FunctionDecl::TemplatedKind::TK_FunctionTemplate == D->getTemplatedKind())
return true;
if(fSelectionRules.IsDeclSelected(D)){
fSelectedFunctions.push_back(D);
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool RScanner::TraverseDeclContextHelper(DeclContext *DC)
{
bool ret = true;
if (!DC)
return true;
clang::Decl* D = dyn_cast<clang::Decl>(DC);
// skip implicit decls
if (D && D->isImplicit()){
return true;
}
if (fScanType == EScanType::kOnePCM){
const clang::NamespaceDecl *parent = llvm::dyn_cast<clang::NamespaceDecl> (DC);
if (parent && 0 == parent->getQualifiedNameAsString().compare(0,5,"std::"))
return true;
}
for (DeclContext::decl_iterator Child = DC->decls_begin(), ChildEnd = DC->decls_end();
ret && (Child != ChildEnd); ++Child) {
ret=TraverseDecl(*Child);
}
return ret;
}
////////////////////////////////////////////////////////////////////////////////
std::string RScanner::GetClassName(clang::DeclContext* DC) const
{
clang::NamedDecl* N=dyn_cast<clang::NamedDecl>(DC);
std::string ret;
if(N && (N->getIdentifier()!=NULL))
ret = N->getNameAsString().c_str();
return ret;
}
////////////////////////////////////////////////////////////////////////////////
bool RScanner::GetDeclName(clang::Decl* D, std::string& name) const
{
clang::NamedDecl* N = dyn_cast<clang::NamedDecl> (D);
if (N) {
name = N->getNameAsString();
return true;
}
else {
name = "UNNAMED";
return false;
}
}
////////////////////////////////////////////////////////////////////////////////
bool RScanner::GetDeclQualName(clang::Decl* D, std::string& qual_name) const
{
clang::NamedDecl* N = dyn_cast<clang::NamedDecl> (D);
if (N) {
llvm::raw_string_ostream stream(qual_name);
N->getNameForDiagnostic(stream,D->getASTContext().getPrintingPolicy(),true); // qual_name = N->getQualifiedNameAsString();
return true;
}
else {
return false;
}
}
////////////////////////////////////////////////////////////////////////////////
bool RScanner::GetFunctionPrototype(clang::Decl* D, std::string& prototype) const {
if (!D) {
return false;
}
clang::FunctionDecl* F = dyn_cast<clang::FunctionDecl> (D);
if (F) {
prototype = "";
for (clang::FunctionDecl::param_iterator I = F->param_begin(), E = F->param_end(); I != E; ++I) {
clang::ParmVarDecl* P = *I;
if (prototype != "")
prototype += ",";
//std::string type = P->getType().getAsString();
std::string type = P->getType().getAsString();
if (type.at(type.length()-1) == '*') {
type.at(type.length()-2) = '*';
type.erase(type.length()-1);
}
prototype += type;
}
prototype = "(" + prototype + ")";
return true;
}
else {
ShowWarning("can't convert Decl to FunctionDecl","");
return false;
}
}
////////////////////////////////////////////////////////////////////////////////
void RScanner::Scan(const clang::ASTContext &C)
{
fSourceManager = &C.getSourceManager();
// if (fVerboseLevel >= 3) fSelectionRules.PrintSelectionRules();
if (fVerboseLevel > 0 && fSelectionRules.GetHasFileNameRule()) {
std::cout<<"File name detected"<<std::endl;
}
if (fScanType == EScanType::kTwoPasses)
TraverseDecl(C.getTranslationUnitDecl());
fFirstPass=false;
fselectedRecordDecls.clear();
fSelectedEnums.clear();
fSelectedTypedefs.clear();
fSelectedFunctions.clear();
TraverseDecl(C.getTranslationUnitDecl());
// And finally resort the results according to the rule ordering.
std::sort(fSelectedClasses.begin(),fSelectedClasses.end());
}
////////////////////////////////////////////////////////////////////////////////
/// Set the callback to the RecordDecl and return the previous one.
RScanner::DeclCallback RScanner::SetRecordDeclCallback(RScanner::DeclCallback callback)
{
DeclCallback old = fRecordDeclCallback;
fRecordDeclCallback = callback;
return old;
}
Computing file changes ...