Logo ROOT  
Reference Guide
BaseSelectionRule.cxx
Go to the documentation of this file.
1// @(#)root/core/utils:$Id: BaseSelectionRule.cxx 41697 2011-11-01 21:03:41Z pcanal $
2// Author: Velislava Spasova September 2010
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// BaseSelectionRule //
15// //
16// Base selection class from which all //
17// selection classes should be derived //
18// //
19//////////////////////////////////////////////////////////////////////////
20
21#include "BaseSelectionRule.h"
22
23#include "TClassEdit.h"
24#include "TClingUtils.h"
25
26#include <iostream>
27#include <string.h>
28#include <cctype>
29
30#include "clang/Basic/SourceLocation.h"
31#include "clang/Basic/SourceManager.h"
32#include "clang/AST/DeclCXX.h"
33#include "clang/AST/ASTContext.h"
34#include "clang/AST/DeclTemplate.h"
35
36#ifdef _WIN32
37#include "process.h"
38#endif
39#include <sys/stat.h>
40
41static const char *R__GetDeclSourceFileName(const clang::Decl* D)
42{
43 clang::ASTContext& ctx = D->getASTContext();
44 clang::SourceManager& SM = ctx.getSourceManager();
45 clang::SourceLocation SL = D->getLocation();
46 // If the class decl is the result of a macpo expansion, take the location
47 // where the macro is "invoked" i.e. expanded at (ExpansionLoc), not the
48 // spelling location (where the delc's tokens come from).
49 if (SL.isMacroID())
50 SL = SM.getExpansionLoc(SL);
51
52 if (SL.isValid() && SL.isFileID()) {
53 clang::PresumedLoc PLoc = SM.getPresumedLoc(SL);
54 return PLoc.getFilename();
55 }
56 else {
57 return "invalid";
58 }
59}
60
61static bool R__match_filename(const char *srcname,const char *filename)
62{
63 if (srcname==nullptr) {
64 return false;
65 }
66 if((strcmp(srcname,filename)==0)) {
67 return true;
68 }
69
70#ifdef G__WIN32
71 char i1name[_MAX_PATH];
72 char fullfile[_MAX_PATH];
73 _fullpath( i1name, srcname, _MAX_PATH );
74 _fullpath( fullfile, filename, _MAX_PATH );
75 if((stricmp(i1name, fullfile)==0)) return 1;
76#else
77 struct stat statBufItem;
78 struct stat statBuf;
79 if ( ( 0 == stat( filename, & statBufItem ) )
80 && ( 0 == stat( srcname, & statBuf ) )
81 && ( statBufItem.st_dev == statBuf.st_dev ) // Files on same device
82 && ( statBufItem.st_ino == statBuf.st_ino ) // Files on same inode (but this is not unique on AFS so we need the next 2 test
83 && ( statBufItem.st_size == statBuf.st_size ) // Files of same size
84 && ( statBufItem.st_mtime == statBuf.st_mtime ) // Files modified at the same time
85 ) {
86 return true;
87 }
88#endif
89 return false;
90}
91
92BaseSelectionRule::BaseSelectionRule(long index, BaseSelectionRule::ESelect sel, const std::string& attributeName, const std::string& attributeValue, cling::Interpreter &interp, const char* selFileName, long lineno)
93 : fIndex(index),fLineNumber(lineno),fSelFileName(selFileName),fIsSelected(sel),fMatchFound(false),fCXXRecordDecl(nullptr),fRequestedType(nullptr),fInterp(&interp)
94{
95 fAttributes.insert(AttributesMap_t::value_type(attributeName, attributeValue));
96}
97
99{
100 fIsSelected = sel;
101}
102
104{
105 return fIsSelected;
106}
107
108bool BaseSelectionRule::HasAttributeWithName(const std::string& attributeName) const
109{
110 AttributesMap_t::const_iterator iter = fAttributes.find(attributeName);
111
112 if(iter!=fAttributes.end()) return true;
113 else return false;
114}
115
116bool BaseSelectionRule::GetAttributeValue(const std::string& attributeName, std::string& returnValue) const
117{
118 AttributesMap_t::const_iterator iter = fAttributes.find(attributeName);
119
120 bool retVal = iter!=fAttributes.end();
121 returnValue = retVal ? iter->second : "";
122 return retVal;
123}
124
125void BaseSelectionRule::SetAttributeValue(const std::string& attributeName, const std::string& attributeValue)
126{
127
128 std::string localAttributeValue(attributeValue);
129
130 int pos = attributeName.find("pattern");
131 int pos_file = attributeName.find("file_pattern");
132
133 // Strip trailing spaces from the name or pattern
134 if (attributeName == "name" || pos> -1){
135 while(std::isspace(*localAttributeValue.begin())) localAttributeValue.erase(localAttributeValue.begin());
136 while(std::isspace(*localAttributeValue.rbegin()))localAttributeValue.erase(localAttributeValue.length()-1);
137 }
138 fAttributes.insert(AttributesMap_t::value_type(attributeName, localAttributeValue));
139
140 if (pos > -1) {
141 if (pos_file > -1) // if we have file_pattern
142 ProcessPattern(localAttributeValue, fFileSubPatterns);
143 else ProcessPattern(localAttributeValue, fSubPatterns); // if we have pattern and proto_pattern
144 }
145
146
147
148}
149
151{
152 return fAttributes;
153}
154
156{
157 Print(std::cout);
158}
159
160void BaseSelectionRule::PrintAttributes(std::ostream &out, int level) const
161{
162 std::string tabs;
163 for (int i = 0; i < level; ++i) {
164 tabs+='\t';
165 }
166
167 if (!fAttributes.empty()) {
168 std::map<std::string,std::string> orderedAttributes(fAttributes.begin(),fAttributes.end());
169 for (auto&& attr : orderedAttributes) {
170 out<<tabs<<attr.first<<" = "<<attr.second<<std::endl;
171 }
172 }
173 else {
174 out<<tabs<<"No attributes"<<std::endl;
175 }
176}
177
179{
180 PrintAttributes(std::cout, level);
181}
182#ifndef G__WIN32
183#include <unistd.h>
184#endif
186 const std::string& name,
187 const std::string& prototype,
188 bool isLinkdef) const
189{
190 /* This method returns whether and how the declaration is matching the rule.
191 * It returns one of:
192 * kNoMatch : the rule does match the declaration
193 * kName : the rule match the declaration by name
194 * kPattern : the rule match the declaration via a pattern
195 * kFile : the declaration's file name is match by the rule (either by name or pattern).
196 * To check whether the rule is accepting or vetoing the declaration see the result of
197 * GetSelected().
198 (
199 * We pass as arguments of the method:
200 * name - the name of the Decl
201 * prototype - the prototype of the Decl (if it is function or method, otherwise "")
202 * file_name - name of the source file
203 * isLinkdef - if the selection rules were generating from a linkdef.h file
204 */
205
206 const std::string& name_value = fName;
207 const std::string& pattern_value = fPattern;
208
209 // Check if we have in hands a typedef to a RecordDecl
210 const clang::CXXRecordDecl *D = llvm::dyn_cast<clang::CXXRecordDecl>(decl);
211 bool isTypedefNametoRecordDecl = false;
212
213 if (!D){
214 //Either it's a CXXRecordDecl ot a TypedefNameDecl
215 const clang::TypedefNameDecl* typedefNameDecl = llvm::dyn_cast<clang::TypedefNameDecl> (decl);
216 isTypedefNametoRecordDecl = typedefNameDecl &&
217 ROOT::TMetaUtils::GetUnderlyingRecordDecl(typedefNameDecl->getUnderlyingType());
218 }
219
220 if (! isTypedefNametoRecordDecl && fCXXRecordDecl !=nullptr && fCXXRecordDecl != (void*)-1) {
221 const clang::CXXRecordDecl *target = fCXXRecordDecl;
222 if ( target && D && target == D ) {
223 // fprintf(stderr,"DECL MATCH: %s %s\n",name_value.c_str(),name.c_str());
224 const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
225 return kName;
226 }
227 } else if (fHasNameAttribute) {
228 if (name_value == name) {
229 const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
230 return kName;
231 } else if (fCXXRecordDecl == nullptr ||
232 (fCXXRecordDecl != (void*)-1 && isTypedefNametoRecordDecl && !decl->hasOwningModule())){
233 // Possibly take the most expensive path if the fCXXRecordDecl is not
234 // set or we already took the expensive path and found nothing (-1).
235 const clang::CXXRecordDecl *target
236 = fHasFromTypedefAttribute ? nullptr : ROOT::TMetaUtils::ScopeSearch(name_value.c_str(), *fInterp,
237 true /*diagnose*/, nullptr);
238
239 if ( target ) {
240 const_cast<BaseSelectionRule*>(this)->fCXXRecordDecl = target;
241 } else {
242 // If the lookup failed, let's not try it again, so mark the value has invalid.
243 const_cast<BaseSelectionRule*>(this)->fCXXRecordDecl = (clang::CXXRecordDecl*)-1;
244 }
245 if ( target && D && target == D ) {
246 const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
247 return kName;
248 }
249 }
250 }
251
252 // do we have matching against the file_name (or file_pattern) attribute and if yes - select or veto
253 const std::string& file_name_value = fFileName;
254 const std::string& file_pattern_value = fFilePattern;
255
257 const char *file_name = R__GetDeclSourceFileName(decl);
258 bool hasFileMatch = ((fHasFileNameAttribute &&
259 //FIXME It would be much better to cache the rule stat result and compare to the clang::FileEntry
260 (R__match_filename(file_name_value.c_str(),file_name))) ||
261 (fHasFilePatternAttribute && CheckPattern(file_name, file_pattern_value, fFileSubPatterns, isLinkdef)));
262
263 if (hasFileMatch) {
264 // Reject utility classes defined in ClassImp
265 // when using a file based rule
266 if (!strncmp(name.c_str(), "R__Init", 7) ||
267 strstr(name.c_str(), "::R__Init")) {
268 return kNoMatch;
269 }
270 if (!name.compare(0, 24, "ROOT::R__dummyintdefault")) {
271 return kNoMatch;
272 }
273 if (!name.compare(0, 27, "ROOT::R__dummyVersionNumber")) {
274 return kNoMatch;
275 }
276 if (!name.compare(0, 22, "ROOT::R__dummyStreamer")) {
277 return kNoMatch;
278 }
279 if (name.find("(anonymous namespace)") != std::string::npos) {
280 // Reject items declared in anonymous namespace
281 return kNoMatch;
282 }
284 if (CheckPattern(name, pattern_value, fSubPatterns, isLinkdef)) {
285 const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
286 return kPattern;
287 }
288 } else {
289 const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
290 return kName;
291 }
292 }
293
294 // We have file_name or file_pattern attribute but the
295 // passed file_name is different than that in the selection rule then return no match
296 return kNoMatch;
297 }
298
300 {
301 bool patternMatched = CheckPattern(name, pattern_value, fSubPatterns, isLinkdef);
302 if (!patternMatched && !isLinkdef) {
303 std::string auxName(name);
304 std::string &nameNoSpaces = auxName;
305 nameNoSpaces.erase(std::remove_if(nameNoSpaces.begin(), nameNoSpaces.end(), isspace),
306 nameNoSpaces.end());
307 if (name.size() != nameNoSpaces.size()) {
308 patternMatched = CheckPattern(nameNoSpaces, pattern_value, fSubPatterns, isLinkdef);
309 }
310
311 // For ROOT-6704: use normalised name for matching if the class is in stl
312 // The reason for this check is that we have rules like std::map<*, int>
313 // We do not know how the internal representation of the innocuous "map"
314 // is. We therefore have to act on a nicer name, obtained with TClassEdit
315 // The check ROOT::TMetaUtils::IsStdDropDefaultClass is there to call
316 // TClassEdit only when necessary as it can be expensive, a performance
317 // optimisation.
318 if (!patternMatched &&
319 D &&
320 //ROOT::TMetaUtils::IsStdDropDefaultClass(*D)) {
323 if (name.size() != auxName.size()) {
324 auxName = TClassEdit::InsertStd(auxName.c_str());
325 patternMatched = CheckPattern(auxName, pattern_value, fSubPatterns, isLinkdef);
326 }
327 }
328
329 }
330 if (patternMatched) {
331 const_cast<BaseSelectionRule *>(this)->SetMatchFound(true);
332 return kPattern;
333 }
334 }
335
336
337 // do we have matching against the proto_name (or proto_pattern) attribute and if yes - select or veto
338 // The following selects functions on whether the requested prototype exactly matches the
339 // prototype issued by SelectionRules::GetFunctionPrototype which relies on
340 // ParmVarDecl::getType()->getAsString()
341 // to get the type names. Currently, this does not print the prototype in the usual
342 // human (written) forms. For example:
343 // For Hash have prototype: '(const class TString &)'
344 // For Hash have prototype: '(const class TString*)'
345 // For Hash have prototype: '(const char*)'
346 // In addition, the const can legally be in various place in the type name and thus
347 // a string based match will be hard to work out (it would need to normalize both
348 // the user input string and the clang provided string).
349 // Using lookup form cling would be probably be a better choice.
350 if (!prototype.empty()) {
351 if (fHasProtoNameAttribute && fProtoName==prototype) {
352 const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
353 return kName;
354 }
355 if (fHasProtoPatternAttribute && CheckPattern(prototype, fProtoPattern, fSubPatterns, isLinkdef)) {
356 const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
357 return kPattern;
358 }
359 }
360
361 return kNoMatch;
362}
363
364
365/*
366 * This method processes the pattern - which means that it splits it in a list of fSubPatterns.
367 * The idea is the following - if we have a pattern = "this*pat*rn", it will be split in the
368 * following list of subpatterns: "this", "pat", "rn". If we have "this*pat\*rn", it will be
369 * split in "this", "pat*rn", i.e. the star could be escaped.
370 */
371
372void BaseSelectionRule::ProcessPattern(const std::string& pattern, std::list<std::string>& out) const
373{
374 std::string temp = pattern;
375 std::string split;
376 int pos;
377 bool escape = false;
378
379 if (pattern.size()==1 && pattern == "*"){
380 out.push_back("");
381 return;
382 }
383
384 while (!temp.empty()){
385 pos = temp.find("*");
386 if (pos == -1) {
387 if (!escape){ // if we don't find a '*', push_back temp (contains the last sub-pattern)
388 out.push_back(temp);
389 // std::cout<<"1. pushed = "<<temp<<std::endl;
390 }
391 else { // if we don't find a star - add temp to split (in split we keep the previous sub-pattern + the last escaped '*')
392 split += temp;
393 out.push_back(split);
394 // std::cout<<"1. pushed = "<<split<<std::endl;
395 }
396 return;
397 }
398 else if (pos == 0) { // we have '*' at the beginning of the pattern; can't have '\' before the '*'
399 temp = temp.substr(1); // remove the '*'
400 }
401 else if (pos == (int)(temp.length()-1)) { // we have '*' at the end of the pattern
402 if (pos > 0 && temp.at(pos-1) == '\\') { // check if we have '\' before the '*'; if yes, we have to escape it
403 split += temp.substr(0, temp.length()-2); // add evrything from the beginning of temp till the '\' to split (where we keep the last sub-pattern)
404 split += temp.at(pos); // add the '*'
405 out.push_back(split); // push_back() split
406 // std::cout<<"3. pushed = "<<split<<std::endl;
407 temp.clear(); // empty temp (the '*' was at the last position of temp, so we don't have anything else to process)
408 }
409 temp = temp.substr(0, (temp.length()-1));
410 }
411 else { // the '*' is at a random position in the pattern
412 if (pos > 0 && temp.at(pos-1) == '\\') { // check if we have '\' before the '*'; if yes, we have to escape it
413 split += temp.substr(0, pos-1); // remove the '\' and add the star to split
414 split += temp.at(pos);
415 escape = true; // escape = true which means that we will add the next sub-pattern to that one
416
417 // DEBUG std::cout<<"temp = "<<temp<<std::endl;
418 temp = temp.substr(pos);
419 // DEBUG std::cout<<"temp = "<<temp<<", split = "<<split<<std::endl;
420 }
421 else { // if we don't have '\' before the '*'
422 if (escape) {
423 split += temp.substr(0, pos);
424 }
425 else {
426 split = temp.substr(0, pos);
427 }
428 escape = false;
429 temp = temp.substr(pos);
430 out.push_back(split);
431 // std::cout<<"2. pushed = "<<split<<std::endl;
432 // DEBUG std::cout<<"temp = "<<temp<<std::endl;
433 split = "";
434 }
435 }
436 // DEBUG std::cout<<"temp = "<<temp<<std::endl;
437 }
438}
439
440/*
441 * This method checks if the given test string is matched against the pattern
442 */
443
444bool BaseSelectionRule::CheckPattern(const std::string& test, const std::string& pattern, const std::list<std::string>& patterns_list, bool isLinkdef) const
445{
446 bool begin = pattern.front() == '*';
447 if (pattern.size() == 1 && begin) {
448 // We have the simple pattern '*', it matches everything by definition!
449 return true;
450 }
451
452 std::list<std::string>::const_iterator it = patterns_list.begin();
453 bool end = pattern.back() == '*';
454
455 // we first check if the last sub-pattern is contained in the test string
456 const std::string& last = patterns_list.back();
457 size_t pos_end = test.rfind(last);
458
459 if (pos_end == std::string::npos) { // the last sub-pattern isn't conatained in the test string
460 return false;
461 }
462 if (!end) { // if the pattern doesn't end with '*', the match has to be complete
463 // i.e. if the last sub-pattern is "sub" the test string should end in "sub" ("1111sub" is OK, "1111sub1" is not OK)
464
465 int len = last.length(); // length of last sub-pattern
466 if ((pos_end+len) < test.length()) {
467 return false;
468 }
469 }
470
471 // position of the first sub-pattern
472 size_t pos1 = test.find(*it);
473
474
475 if (pos1 == std::string::npos || (!begin && pos1 != 0)) { // if the first sub-pattern isn't found in test or if it is found but the
476 // pattern doesn't start with '*' and the sub-pattern is not at the first position
477 //std::cout<<"\tNo match!"<<std::endl;
478 return false;
479 }
480
481 if (isLinkdef) { // A* selects all global classes, unions, structs but not the nested, i.e. not A::B
482 // A::* selects the nested classes
483 int len = (*it).length();
484 int pos_colon = test.find("::", pos1+len);
485
486 if (pos_colon > -1) {
487 return false;
488 }
489
490 }
491
492 if (patterns_list.size() > 1) {
493 if (((*it).length())+pos1 > pos_end) {
494 // std::cout<<"\tNo match";
495 return false; // end is contained in begin -> test = "A::B" sub-patterns = "A::", "::" will return false
496 }
497 }
498
499
500 ++it;
501
502 for (; it != patterns_list.end(); ++it) {
503 // std::cout<<"sub-pattern = "<<*it<<std::endl;
504 size_t pos2 = test.find(*it);
505 if (pos2 <= pos1) {
506 return false;
507 }
508 pos1 = pos2;
509 }
510
511 return true;
512}
513
514
516{
517 fMatchFound = match;
518}
519
521{
522 return fMatchFound;
523}
524
526{
527 return fRequestedType;
528}
529
530void BaseSelectionRule::SetCXXRecordDecl(const clang::CXXRecordDecl *decl, const clang::Type *typeptr)
531{
532 fCXXRecordDecl = decl;
533 fRequestedType = typeptr;
534}
535
537{
538 std::string value;
545 fHasFromTypedefAttribute = GetAttributeValue("fromTypedef",value);
546 fIsFromTypedef = (value == "true");
547
549
550
552 if (fSubPatterns.empty()) {
553 std::cout<<"Error - A pattern selection without sub patterns." <<std::endl;
554 }
555 }
556
557}
558
559
static bool R__match_filename(const char *srcname, const char *filename)
static const char * R__GetDeclSourceFileName(const clang::Decl *D)
char name[80]
Definition: TGX11.cxx:110
std::string fFilePattern
void SetAttributeValue(const std::string &attributeName, const std::string &attributeValue)
bool GetAttributeValue(const std::string &attributeName, std::string &returnValue) const
bool CheckPattern(const std::string &test, const std::string &pattern, const std::list< std::string > &patterns_list, bool isLinkdef) const
void SetSelected(ESelect sel)
void ProcessPattern(const std::string &pattern, std::list< std::string > &out) const
const clang::CXXRecordDecl * fCXXRecordDecl
std::string fProtoName
std::list< std::string > fSubPatterns
virtual void DebugPrint() const
EMatchType Match(const clang::NamedDecl *decl, const std::string &name, const std::string &prototype, bool isLinkdef) const
void SetCXXRecordDecl(const clang::CXXRecordDecl *decl, const clang::Type *typeptr)
bool GetMatchFound() const
const clang::Type * GetRequestedType() const
std::string fProtoPattern
const AttributesMap_t & GetAttributes() const
ESelect GetSelected() const
bool HasAttributeWithName(const std::string &attributeName) const
AttributesMap_t fAttributes
std::string fNArgsToKeep
cling::Interpreter * fInterp
const clang::Type * fRequestedType
void SetMatchFound(bool match)
std::unordered_map< std::string, std::string > AttributesMap_t
void PrintAttributes(int level) const
std::list< std::string > fFileSubPatterns
BaseSelectionRule(ESelect sel)
virtual void Print(std::ostream &out) const =0
Type
enumeration specifying the integration types.
static const std::string pattern("pattern")
static const std::string nArgsToKeep("nArgsToKeep")
clang::RecordDecl * GetUnderlyingRecordDecl(clang::QualType type)
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'.
std::string InsertStd(const char *tname)
ROOT::ESTLType IsSTLCont(std::string_view type)
type : type name: vector<list<classA,allocator>,allocator> result: 0 : not stl container code of cont...
void GetNormalizedName(std::string &norm_name, std::string_view name)
Return the normalized name.
Definition: TClassEdit.cxx:837
Definition: test.py:1