Exemple #1
0
// TODO: Add classes with a finalize() method that specialize FinalizerTrait.
bool RecordInfo::NeedsFinalization() {
  if (does_need_finalization_ == kNotComputed) {
    // Rely on hasNonTrivialDestructor(), but if the only
    // identifiable reason for it being true is the presence
    // of a safely ignorable class as a direct base,
    // or we're processing such an 'ignorable' class, then it does
    // not need finalization.
    does_need_finalization_ =
        record_->hasNonTrivialDestructor() ? kTrue : kFalse;
    if (!does_need_finalization_)
      return does_need_finalization_;

    CXXDestructorDecl* dtor = record_->getDestructor();
    if (dtor && dtor->isUserProvided())
      return does_need_finalization_;
    for (Fields::iterator it = GetFields().begin();
         it != GetFields().end();
         ++it) {
      if (it->second.edge()->NeedsFinalization())
        return does_need_finalization_;
    }

    for (Bases::iterator it = GetBases().begin();
         it != GetBases().end();
         ++it) {
      if (it->second.info()->NeedsFinalization())
        return does_need_finalization_;
    }
    // Destructor was non-trivial due to bases with destructors that
    // can be safely ignored. Hence, no need for finalization.
    does_need_finalization_ = kFalse;
  }
  return does_need_finalization_;
}
bool BlinkGCPluginConsumer::HasNonEmptyFinalizer(RecordInfo* info) {
  CXXDestructorDecl* dtor = info->record()->getDestructor();
  if (dtor && dtor->isUserProvided()) {
    if (!dtor->hasBody() || !EmptyStmtVisitor::isEmpty(dtor->getBody()))
      return true;
  }
  for (RecordInfo::Bases::iterator it = info->GetBases().begin();
       it != info->GetBases().end();
       ++it) {
    if (HasNonEmptyFinalizer(it->second.info()))
      return true;
  }
  for (RecordInfo::Fields::iterator it = info->GetFields().begin();
       it != info->GetFields().end();
       ++it) {
    if (it->second.edge()->NeedsFinalization())
      return true;
  }
  return false;
}
// TODO: Should we collect destructors similar to trace methods?
void BlinkGCPluginConsumer::CheckFinalization(RecordInfo* info) {
  CXXDestructorDecl* dtor = info->record()->getDestructor();

  // For finalized classes, check the finalization method if possible.
  if (info->IsGCFinalized()) {
    if (dtor && dtor->hasBody()) {
      CheckFinalizerVisitor visitor(&cache_, info->IsEagerlyFinalized());
      visitor.TraverseCXXMethodDecl(dtor);
      if (!visitor.finalized_fields().empty()) {
        ReportFinalizerAccessesFinalizedFields(
            dtor, &visitor.finalized_fields());
      }
    }
    return;
  }

  // Don't require finalization of a mixin that has not yet been "mixed in".
  if (info->IsGCMixin())
    return;

  // Report the finalization error, and proceed to print possible causes for
  // the finalization requirement.
  ReportClassRequiresFinalization(info);

  if (dtor && dtor->isUserProvided())
    NoteUserDeclaredDestructor(dtor);

  for (RecordInfo::Bases::iterator it = info->GetBases().begin();
       it != info->GetBases().end();
       ++it) {
    if (it->second.info()->NeedsFinalization())
      NoteBaseRequiresFinalization(&it->second);
  }

  for (RecordInfo::Fields::iterator it = info->GetFields().begin();
       it != info->GetFields().end();
       ++it) {
    if (it->second.edge()->NeedsFinalization())
      NoteField(&it->second, diag_field_requires_finalization_note_);
  }
}
void VirtualCallsFromCTOR::VisitDecl(Decl *decl)
{
    CXXConstructorDecl *ctorDecl = dyn_cast<CXXConstructorDecl>(decl);
    CXXDestructorDecl *dtorDecl = dyn_cast<CXXDestructorDecl>(decl);
    if (ctorDecl == nullptr && dtorDecl == nullptr)
        return;

    Stmt *ctorOrDtorBody = ctorDecl ? ctorDecl->getBody() : dtorDecl->getBody();
    if (ctorOrDtorBody == nullptr)
        return;

    CXXRecordDecl *classDecl = ctorDecl ? ctorDecl->getParent() : dtorDecl->getParent();

    std::vector<Stmt*> processedStmts;
    SourceLocation loc = containsVirtualCall(classDecl, ctorOrDtorBody, processedStmts);
    if (loc.isValid()) {
        if (ctorDecl != nullptr) {
            emitWarning(decl->getLocStart(), "Calling pure virtual function in CTOR");
        } else {
            emitWarning(decl->getLocStart(), "Calling pure virtual function in DTOR");
        }
        emitWarning(loc, "Called here");
    }
}
Exemple #5
0
OMPClause *Sema::ActOnOpenMPFirstprivateClause(ArrayRef<Expr *> VarList,
                                               SourceLocation StartLoc,
                                               SourceLocation LParenLoc,
                                               SourceLocation EndLoc) {
  SmallVector<Expr *, 8> Vars;
  for (ArrayRef<Expr *>::iterator I = VarList.begin(), E = VarList.end();
       I != E; ++I) {
    assert(*I && "NULL expr in OpenMP firstprivate clause.");
    if (isa<DependentScopeDeclRefExpr>(*I)) {
      // It will be analyzed later.
      Vars.push_back(*I);
      continue;
    }

    SourceLocation ELoc = (*I)->getExprLoc();
    // OpenMP [2.1, C/C++]
    //  A list item is a variable name.
    // OpenMP  [2.9.3.3, Restrictions, p.1]
    //  A variable that is part of another variable (as an array or
    //  structure element) cannot appear in a private clause.
    DeclRefExpr *DE = dyn_cast_or_null<DeclRefExpr>(*I);
    if (!DE || !isa<VarDecl>(DE->getDecl())) {
      Diag(ELoc, diag::err_omp_expected_var_name)
        << (*I)->getSourceRange();
      continue;
    }
    Decl *D = DE->getDecl();
    VarDecl *VD = cast<VarDecl>(D);

    QualType Type = VD->getType();
    if (Type->isDependentType() || Type->isInstantiationDependentType()) {
      // It will be analyzed later.
      Vars.push_back(DE);
      continue;
    }

    // OpenMP [2.9.3.3, Restrictions, C/C++, p.3]
    //  A variable that appears in a private clause must not have an incomplete
    //  type or a reference type.
    if (RequireCompleteType(ELoc, Type,
                            diag::err_omp_firstprivate_incomplete_type)) {
      continue;
    }
    if (Type->isReferenceType()) {
      Diag(ELoc, diag::err_omp_clause_ref_type_arg)
        << getOpenMPClauseName(OMPC_firstprivate) << Type;
      bool IsDecl = VD->isThisDeclarationADefinition(Context) ==
                    VarDecl::DeclarationOnly;
      Diag(VD->getLocation(), IsDecl ? diag::note_previous_decl :
                                       diag::note_defined_here) << VD;
      continue;
    }

    // OpenMP [2.9.3.4, Restrictions, C/C++, p.1]
    //  A variable of class type (or array thereof) that appears in a private
    //  clause requires an accesible, unambiguous copy constructor for the
    //  class type.
    Type = Context.getBaseElementType(Type);
    CXXRecordDecl *RD = getLangOpts().CPlusPlus ?
                          Type.getNonReferenceType()->getAsCXXRecordDecl() : 0;
    if (RD) {
      CXXConstructorDecl *CD = LookupCopyingConstructor(RD, 0);
      PartialDiagnostic PD =
        PartialDiagnostic(PartialDiagnostic::NullDiagnostic());
      if (!CD ||
          CheckConstructorAccess(ELoc, CD,
                                 InitializedEntity::InitializeTemporary(Type),
                                 CD->getAccess(), PD) == AR_inaccessible ||
          CD->isDeleted()) {
        Diag(ELoc, diag::err_omp_required_method)
             << getOpenMPClauseName(OMPC_firstprivate) << 1;
        bool IsDecl = VD->isThisDeclarationADefinition(Context) ==
                      VarDecl::DeclarationOnly;
        Diag(VD->getLocation(), IsDecl ? diag::note_previous_decl :
                                         diag::note_defined_here) << VD;
        Diag(RD->getLocation(), diag::note_previous_decl) << RD;
        continue;
      }
      MarkFunctionReferenced(ELoc, CD);
      DiagnoseUseOfDecl(CD, ELoc);

      CXXDestructorDecl *DD = RD->getDestructor();
      if (DD) {
        if (CheckDestructorAccess(ELoc, DD, PD) == AR_inaccessible ||
            DD->isDeleted()) {
          Diag(ELoc, diag::err_omp_required_method)
               << getOpenMPClauseName(OMPC_firstprivate) << 4;
          bool IsDecl = VD->isThisDeclarationADefinition(Context) ==
                        VarDecl::DeclarationOnly;
          Diag(VD->getLocation(), IsDecl ? diag::note_previous_decl :
                                           diag::note_defined_here) << VD;
          Diag(RD->getLocation(), diag::note_previous_decl) << RD;
          continue;
        }
        MarkFunctionReferenced(ELoc, DD);
        DiagnoseUseOfDecl(DD, ELoc);
      }
    }

    // If StartLoc and EndLoc are invalid - this is an implicit firstprivate
    // variable and it was checked already.
    if (StartLoc.isValid() && EndLoc.isValid()) {
      DSAStackTy::DSAVarData DVar = DSAStack->getTopDSA(VD);
      Type = Type.getNonReferenceType().getCanonicalType();
      bool IsConstant = Type.isConstant(Context);
      Type = Context.getBaseElementType(Type);
      // OpenMP [2.4.13, Data-sharing Attribute Clauses]
      //  A list item that specifies a given variable may not appear in more
      // than one clause on the same directive, except that a variable may be
      //  specified in both firstprivate and lastprivate clauses.
      //  TODO: add processing for lastprivate.
      if (DVar.CKind != OMPC_unknown && DVar.CKind != OMPC_firstprivate &&
          DVar.RefExpr) {
        Diag(ELoc, diag::err_omp_wrong_dsa)
           << getOpenMPClauseName(DVar.CKind)
           << getOpenMPClauseName(OMPC_firstprivate);
        Diag(DVar.RefExpr->getExprLoc(), diag::note_omp_explicit_dsa)
           << getOpenMPClauseName(DVar.CKind);
        continue;
      }

      // OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced
      // in a Construct]
      //  Variables with the predetermined data-sharing attributes may not be
      //  listed in data-sharing attributes clauses, except for the cases
      //  listed below. For these exceptions only, listing a predetermined
      //  variable in a data-sharing attribute clause is allowed and overrides
      //  the variable's predetermined data-sharing attributes.
      // OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced
      // in a Construct, C/C++, p.2]
      //  Variables with const-qualified type having no mutable member may be
      //  listed in a firstprivate clause, even if they are static data members.
      if (!(IsConstant || VD->isStaticDataMember()) && !DVar.RefExpr &&
          DVar.CKind != OMPC_unknown && DVar.CKind != OMPC_shared) {
        Diag(ELoc, diag::err_omp_wrong_dsa)
           << getOpenMPClauseName(DVar.CKind)
           << getOpenMPClauseName(OMPC_firstprivate);
        Diag(VD->getLocation(), diag::note_omp_predetermined_dsa)
           << getOpenMPClauseName(DVar.CKind);
        continue;
      }

      // OpenMP [2.9.3.4, Restrictions, p.2]
      //  A list item that is private within a parallel region must not appear
      //  in a firstprivate clause on a worksharing construct if any of the
      //  worksharing regions arising from the worksharing construct ever bind
      //  to any of the parallel regions arising from the parallel construct.
      // OpenMP [2.9.3.4, Restrictions, p.3]
      //  A list item that appears in a reduction clause of a parallel construct
      //  must not appear in a firstprivate clause on a worksharing or task
      //  construct if any of the worksharing or task regions arising from the
      //  worksharing or task construct ever bind to any of the parallel regions
      //  arising from the parallel construct.
      // OpenMP [2.9.3.4, Restrictions, p.4]
      //  A list item that appears in a reduction clause in worksharing
      //  construct must not appear in a firstprivate clause in a task construct
      //  encountered during execution of any of the worksharing regions arising
      //  from the worksharing construct.
      // TODO:
    }

    DSAStack->addDSA(VD, DE, OMPC_firstprivate);
    Vars.push_back(DE);
  }

  if (Vars.empty()) return 0;

  return OMPFirstprivateClause::Create(Context, StartLoc, LParenLoc, EndLoc,
                                       Vars);
}
Exemple #6
0
OMPClause *Sema::ActOnOpenMPPrivateClause(ArrayRef<Expr *> VarList,
                                          SourceLocation StartLoc,
                                          SourceLocation LParenLoc,
                                          SourceLocation EndLoc) {
  SmallVector<Expr *, 8> Vars;
  for (ArrayRef<Expr *>::iterator I = VarList.begin(), E = VarList.end();
       I != E; ++I) {
    assert(*I && "NULL expr in OpenMP private clause.");
    if (isa<DependentScopeDeclRefExpr>(*I)) {
      // It will be analyzed later.
      Vars.push_back(*I);
      continue;
    }

    SourceLocation ELoc = (*I)->getExprLoc();
    // OpenMP [2.1, C/C++]
    //  A list item is a variable name.
    // OpenMP  [2.9.3.3, Restrictions, p.1]
    //  A variable that is part of another variable (as an array or
    //  structure element) cannot appear in a private clause.
    DeclRefExpr *DE = dyn_cast_or_null<DeclRefExpr>(*I);
    if (!DE || !isa<VarDecl>(DE->getDecl())) {
      Diag(ELoc, diag::err_omp_expected_var_name)
        << (*I)->getSourceRange();
      continue;
    }
    Decl *D = DE->getDecl();
    VarDecl *VD = cast<VarDecl>(D);

    QualType Type = VD->getType();
    if (Type->isDependentType() || Type->isInstantiationDependentType()) {
      // It will be analyzed later.
      Vars.push_back(DE);
      continue;
    }

    // OpenMP [2.9.3.3, Restrictions, C/C++, p.3]
    //  A variable that appears in a private clause must not have an incomplete
    //  type or a reference type.
    if (RequireCompleteType(ELoc, Type,
                            diag::err_omp_private_incomplete_type)) {
      continue;
    }
    if (Type->isReferenceType()) {
      Diag(ELoc, diag::err_omp_clause_ref_type_arg)
        << getOpenMPClauseName(OMPC_private) << Type;
      bool IsDecl = VD->isThisDeclarationADefinition(Context) ==
                    VarDecl::DeclarationOnly;
      Diag(VD->getLocation(), IsDecl ? diag::note_previous_decl :
                                       diag::note_defined_here) << VD;
      continue;
    }

    // OpenMP [2.9.3.3, Restrictions, C/C++, p.1]
    //  A variable of class type (or array thereof) that appears in a private
    //  clause requires an accesible, unambiguous default constructor for the
    //  class type.
    while (Type.getNonReferenceType()->isArrayType()) {
      Type = cast<ArrayType>(
                 Type.getNonReferenceType().getTypePtr())->getElementType();
    }
    CXXRecordDecl *RD = getLangOpts().CPlusPlus ?
                          Type.getNonReferenceType()->getAsCXXRecordDecl() : 0;
    if (RD) {
      CXXConstructorDecl *CD = LookupDefaultConstructor(RD);
      PartialDiagnostic PD =
        PartialDiagnostic(PartialDiagnostic::NullDiagnostic());
      if (!CD ||
          CheckConstructorAccess(ELoc, CD,
                                 InitializedEntity::InitializeTemporary(Type),
                                 CD->getAccess(), PD) == AR_inaccessible ||
          CD->isDeleted()) {
        Diag(ELoc, diag::err_omp_required_method)
             << getOpenMPClauseName(OMPC_private) << 0;
        bool IsDecl = VD->isThisDeclarationADefinition(Context) ==
                      VarDecl::DeclarationOnly;
        Diag(VD->getLocation(), IsDecl ? diag::note_previous_decl :
                                         diag::note_defined_here) << VD;
        Diag(RD->getLocation(), diag::note_previous_decl) << RD;
        continue;
      }
      MarkFunctionReferenced(ELoc, CD);
      DiagnoseUseOfDecl(CD, ELoc);

      CXXDestructorDecl *DD = RD->getDestructor();
      if (DD) {
        if (CheckDestructorAccess(ELoc, DD, PD) == AR_inaccessible ||
            DD->isDeleted()) {
          Diag(ELoc, diag::err_omp_required_method)
               << getOpenMPClauseName(OMPC_private) << 4;
          bool IsDecl = VD->isThisDeclarationADefinition(Context) ==
                        VarDecl::DeclarationOnly;
          Diag(VD->getLocation(), IsDecl ? diag::note_previous_decl :
                                           diag::note_defined_here) << VD;
          Diag(RD->getLocation(), diag::note_previous_decl) << RD;
          continue;
        }
        MarkFunctionReferenced(ELoc, DD);
        DiagnoseUseOfDecl(DD, ELoc);
      }
    }

    // OpenMP [2.9.1.1, Data-sharing Attribute Rules for Variables Referenced
    // in a Construct]
    //  Variables with the predetermined data-sharing attributes may not be
    //  listed in data-sharing attributes clauses, except for the cases
    //  listed below. For these exceptions only, listing a predetermined
    //  variable in a data-sharing attribute clause is allowed and overrides
    //  the variable's predetermined data-sharing attributes.
    DSAStackTy::DSAVarData DVar = DSAStack->getTopDSA(VD);
    if (DVar.CKind != OMPC_unknown && DVar.CKind != OMPC_private) {
      Diag(ELoc, diag::err_omp_wrong_dsa)
         << getOpenMPClauseName(DVar.CKind)
         << getOpenMPClauseName(OMPC_private);
      if (DVar.RefExpr) {
        Diag(DVar.RefExpr->getExprLoc(), diag::note_omp_explicit_dsa)
             << getOpenMPClauseName(DVar.CKind);
      } else {
        Diag(VD->getLocation(), diag::note_omp_predetermined_dsa)
             << getOpenMPClauseName(DVar.CKind);
      }
      continue;
    }

    DSAStack->addDSA(VD, DE, OMPC_private);
    Vars.push_back(DE);
  }

  if (Vars.empty()) return 0;

  return OMPPrivateClause::Create(Context, StartLoc, LParenLoc, EndLoc, Vars);
}
bool isFunctionDef(const Decl* D){
	string node = D->getDeclKindName();

	CXXDestructorDecl* CD = (CXXDestructorDecl*) D;
	return ((node == "CXXConstructor" || node == "CXXDestructor" || node == "CXXMethod") && !CD->isImplicit());
}
		/*
		   this helper function is called when the traversal reaches a node of type Decl
		 */
		bool DeclHelper(Decl *D){

			const Stmt* parent = getStmtParent(D, Context);
			//const Stmt* parentsParent = getStmtParent(parent, Context);

			//if it is part of the (init; condition; increment) of a for loop, we don't care about it
			if(isFlowControl(D, Context)){
				return false;
			}


			//supresses the catch stmt's arguments
			if(parent != NULL && strcmp(parent->getStmtClassName(), "CXXCatchStmt") == 0){
				return true;
			}


			string filename;
			if(!isInCurFile(Context, D, filename) && filename.size() != 0){
				return false;
			}else if(filename.size() == 0){
				return true;
			}



			string output = "";
			//get the name of the node type
			string node = D->getDeclKindName();
			//calculate the current level, nextLevel, and previousLevel
			int intLevel = getLevelDecl(D);int intNextLevel = intLevel+1;
			int intNextNextLevel = intLevel+2; int intPrevLevel = intLevel-1;
			//create string values for the levels to use as output
			string level; string nextLevel;
			string nextNextLevel; string prevLevel;
			stringstream ss; stringstream ss2; stringstream ss3; stringstream ss4;
			ss << intLevel;
			level = ss.str();
			ss2 << intNextLevel;
			nextLevel = ss2.str();
			ss3 << intPrevLevel;
			prevLevel = ss3.str();
			ss4 << intNextNextLevel;
			nextNextLevel = ss4.str();



			if(callStackDebug && !callStack.empty()){
				cerr << "decl: call stack top: " << callStack.top()->getStmtClassName() << endl;
			}

			//if top of stack is no longer a parent
			while(!callStack.empty() && numClosingArgsNeeded > 0
					&& !isParentDecl(D, callStack.top()->getStmtClassName())){

				if(debugPrint){
					cerr << "adding args" << endl;
				}
				numClosingArgsNeeded--;
				output += "</args,1>\n";

				callStack.pop();
				if(callStackDebug){
					cerr << "poping" << endl;
					printCallStack();
				}
			}


			//add new calls to stack
			if(isParentDeclInCurFile(D,"CXXConstructExpr") && isParentDecl(D, "CXXConstructExpr")){

				if(debugPrint){
					cerr << "setting previousConstructorCall to true" << endl;
				}


			}else if(isParentDeclInCurFile(D,"CXXTemporaryObjectExpr") && isParentDecl(D, "CXXTemporaryObjectExpr")){

				if(debugPrint){
					cerr << "setting previousTempConstructorCallArg" << endl;
				}


			}else if(isParentDecl(D, "CallExpr")){

				if(debugPrint){
					cerr << "setting previousCallArgs to true" << endl;
				}


			}else if(isParentDecl(D, "CXXMemberCallExpr")){

				if(debugPrint){
					cerr << "setting previousMemberCallArg to true" << endl;
				}

			}


			if(isParentDecl(getDeclParent(D, Context), "Var")){
				previousRhsDecl = true;
				if(debugPrint){
					cout << "setting prev var to true" << endl;
				}
			}else if(previousRhsDecl && numClosingVarsNeeded > 0){
				//if the current node is not a child of a variable declaration 
				//but the previous node was a child of a variable declation 
				//then we know to print a </decl>
				output +="</variableDecl,1>\n";
				numClosingVarsNeeded--;
				previousRhsDecl = false;
			}


			if(node == "Var"){
				output += "<variableDecl, " + prevLevel +  ">";
				numClosingVarsNeeded++;
				VarDecl* VD = (VarDecl*) D;
				if(!VD->hasInit()){
					output +="\n</variableDecl,1>\n";
					numClosingVarsNeeded--;
				}
			}else if(node == "Function"){
				FunctionDecl* FD = (FunctionDecl*) D; 
				output += "<functionDef," + level +">";
				//add function name to the output
				output += "\n<name: " + FD->getNameInfo().getAsString()
					+ "," + nextLevel + ">";

			}else if(node == "CXXRecord"){
				const Decl* parent = getDeclParent(D, Context);
				if(parent && strcmp(parent->getDeclKindName(), "CXXRecord") != 0){
					CXXRecordDecl* CD = (CXXRecordDecl*) D;
					output += "<classDef," + level + ">";
					output += "\n<name: " + CD->getNameAsString() + "," + nextLevel + ">";
					output += "\n<bases," + nextLevel + ">";

					//iterate over all bases and add them to the output
					CXXRecordDecl::base_class_iterator basesItr =  CD->bases_begin();
					while(basesItr != CD->bases_end()){
						QualType qt = basesItr->getType();
						output +=  "\n<base: " +  qt.getBaseTypeIdentifier()->getName().str();
						output +=  "," + nextNextLevel + ">";
						basesItr++;
					}

					//iterate over all of the virtual bases and add them to the output
					auto vBasesItr = CD->vbases_begin();
					while(vBasesItr != CD->vbases_end()){
						QualType qt = vBasesItr->getType();
						output +=  "\n<base: " +  qt.getBaseTypeIdentifier()->getName().str();
						output +=  "," + nextNextLevel + ">";
						vBasesItr++;
					}

				}
			}else if(node == "CXXDestructor"){
				CXXDestructorDecl* CD = (CXXDestructorDecl*) D;
				if(!CD->isImplicit()){
					output += "<functionDef," + level +">";
					//add function name to the output
					output += "\n<name: ~" + CD->getNameInfo().getAsString()
						+ "," + nextLevel + ">";
				}


			}else if(node == "CXXConstructor"){
				CXXConstructorDecl* CD = (CXXConstructorDecl*) D;
				if(!CD->isImplicit()){
					output += "<functionDef," + level +">";
					//add function name to the output
					output += "\n<name: " + CD->getNameInfo().getAsString()
						+ "," + nextLevel + ">";
				}
			}else if(node == "CXXMethod"){
				CXXMethodDecl* CM = (CXXMethodDecl*) D;
				if(!CM->isImplicit()){
					output += "<functionDef," + level +">";
					//add function name to the output
					output += "\n<name: " + CM->getNameInfo().getAsString()
						+ "," + nextLevel + ">";
				}
			}else{

				if(debugPrint){
					output += "<";
					output += node;
					output += ">";
				}
			}

			if(output.size() != 0){
				cout << output << endl;
			}

			return true;
		}