Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:31:50

0001 // ClassChecker.cpp by Patrick Gartung (gartung@fnal.gov)
0002 //
0003 // Objectives of this checker
0004 //
0005 // For each special function of a class (produce, beginrun, endrun, beginlumi, endlumi)
0006 //
0007 //     1) identify member data being modified
0008 //          built-in types reseting values
0009 //          calling non-const member function object if member data is an object
0010 //     2) for each non-const member functions of self called
0011 //          do 1) above
0012 //     3) for each non member function (external) passed in a member object
0013 //          complain if arguement passed in by non-const ref
0014 //          pass by value OK
0015 //          pass by const ref & pointer OK
0016 //
0017 //
0018 #include <clang/AST/Decl.h>
0019 #include <clang/AST/Attr.h>
0020 #include <clang/AST/DeclTemplate.h>
0021 #include <clang/AST/DeclCXX.h>
0022 #include <clang/AST/StmtVisitor.h>
0023 #include <clang/AST/ParentMap.h>
0024 #include <clang/Analysis/CFGStmtMap.h>
0025 #include <clang/StaticAnalyzer/Core/Checker.h>
0026 #include <clang/StaticAnalyzer/Core/BugReporter/BugReporter.h>
0027 #include <clang/StaticAnalyzer/Core/BugReporter/BugType.h>
0028 #include <clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h>
0029 #include <llvm/Support/SaveAndRestore.h>
0030 #include <llvm/ADT/SmallString.h>
0031 
0032 #include "ClassChecker.h"
0033 #include <iostream>
0034 #include <fstream>
0035 #include <iterator>
0036 #include <string>
0037 #include <algorithm>
0038 
0039 using namespace clang;
0040 using namespace clang::ento;
0041 using namespace llvm;
0042 
0043 namespace clangcms {
0044 
0045   void writeLog(std::string ostring) {
0046     std::string tname = "class-checker.txt.unsorted";
0047     support::writeLog(ostring, tname);
0048     return;
0049   }
0050 
0051   class WalkAST : public clang::StmtVisitor<WalkAST> {
0052     const CheckerBase *Checker;
0053     clang::ento::BugReporter &BR;
0054     clang::AnalysisDeclContext *AC;
0055     const CXXMethodDecl *AD;
0056     typedef const clang::CXXMemberCallExpr *WorkListUnit;
0057     typedef clang::SmallVector<WorkListUnit, 50> DFSWorkList;
0058 
0059     /// A vector representing the worklist which has a chain of CallExprs.
0060     DFSWorkList WList;
0061 
0062     // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the
0063     // body has not been visited yet.
0064     // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the
0065     // body has been visited.
0066     enum Kind {
0067       NotVisited,
0068       Visiting, /**< A CallExpr to this FunctionDecl is in the 
0069                                 worklist, but the body has not yet been
0070                                 visited. */
0071       Visited   /**< A CallExpr to this FunctionDecl is in the
0072                                 worklist, and the body has been visited. */
0073     };
0074 
0075     /// A DenseMap that records visited states of FunctionDecls.
0076     llvm::DenseMap<const clang::CXXMemberCallExpr *, Kind> VisitedFunctions;
0077 
0078     /// The CallExpr whose body is currently being visited.  This is used for
0079     /// generating bug reports.  This is null while visiting the body of a
0080     /// constructor or destructor.
0081     const clang::CXXMemberCallExpr *visitingCallExpr;
0082     const char *sfile;
0083 
0084   public:
0085     WalkAST(const CheckerBase *checker,
0086             clang::ento::BugReporter &br,
0087             clang::AnalysisDeclContext *ac,
0088             const CXXMethodDecl *fd,
0089             const char *file)
0090         : Checker(checker), BR(br), AC(ac), AD(fd), visitingCallExpr(nullptr), sfile(file) {}
0091 
0092     bool hasWork() const { return !WList.empty(); }
0093 
0094     /// This method adds a CallExpr to the worklist
0095     void Enqueue(WorkListUnit WLUnit) {
0096       Kind &K = VisitedFunctions[WLUnit];
0097       if ((K = Visiting))
0098         return;
0099       K = Visiting;
0100       WList.push_back(WLUnit);
0101     }
0102 
0103     /// This method returns an item from the worklist without removing it.
0104     WorkListUnit Dequeue() {
0105       assert(!WList.empty());
0106       return WList.back();
0107     }
0108 
0109     void Execute() {
0110       if (WList.empty())
0111         return;
0112       WorkListUnit WLUnit = Dequeue();
0113       const clang::CXXMethodDecl *FD = WLUnit->getMethodDecl();
0114       if (!FD)
0115         return;
0116       llvm::SaveAndRestore<const clang::CXXMemberCallExpr *> SaveCall(visitingCallExpr, WLUnit);
0117       if (FD && FD->hasBody())
0118         Visit(FD->getBody());
0119       VisitedFunctions[WLUnit] = Visited;
0120       WList.pop_back();
0121     }
0122 
0123     const clang::Stmt *ParentStmt(const Stmt *S) {
0124       const Stmt *P = AC->getParentMap().getParentIgnoreParens(S);
0125       if (!P)
0126         return nullptr;
0127       return P;
0128     }
0129 
0130     void WListDump(llvm::raw_ostream &os) {
0131       clang::LangOptions LangOpts;
0132       LangOpts.CPlusPlus = true;
0133       clang::PrintingPolicy Policy(LangOpts);
0134       if (!WList.empty()) {
0135         for (llvm::SmallVectorImpl<const clang::CXXMemberCallExpr *>::iterator I = WList.begin(), E = WList.end();
0136              I != E;
0137              I++) {
0138           (*I)->printPretty(os, nullptr, Policy);
0139           os << " ";
0140         }
0141       }
0142     }
0143 
0144     // Stmt visitor methods.
0145     void VisitChildren(clang::Stmt *S);
0146     void VisitStmt(clang::Stmt *S) { VisitChildren(S); }
0147     void VisitMemberExpr(clang::MemberExpr *E);
0148     void VisitCXXMemberCallExpr(clang::CXXMemberCallExpr *CE);
0149     void VisitDeclRefExpr(clang::DeclRefExpr *DRE);
0150     void VisitCXXConstCastExpr(clang::CXXConstCastExpr *CCE);
0151     void ReportDeclRef(const clang::DeclRefExpr *DRE);
0152     void CheckCXXOperatorCallExpr(const clang::CXXOperatorCallExpr *CE, const clang::MemberExpr *E);
0153     void CheckBinaryOperator(const clang::BinaryOperator *BO, const clang::MemberExpr *E);
0154     void CheckUnaryOperator(const clang::UnaryOperator *UO, const clang::MemberExpr *E);
0155     void CheckExplicitCastExpr(const clang::ExplicitCastExpr *CE, const clang::MemberExpr *E);
0156     void CheckReturnStmt(const clang::ReturnStmt *RS, const clang::MemberExpr *E);
0157     void ReportCast(const clang::ExplicitCastExpr *CE);
0158     void ReportCall(const clang::CXXMemberCallExpr *CE);
0159     void ReportMember(const clang::MemberExpr *ME);
0160     void ReportCallReturn(const clang::ReturnStmt *RS);
0161     void ReportCallArg(const clang::CXXMemberCallExpr *CE, const int i);
0162   };
0163 
0164   //===----------------------------------------------------------------------===//
0165   // AST walking.
0166   //===----------------------------------------------------------------------===//
0167 
0168   void WalkAST::VisitChildren(clang::Stmt *S) {
0169     for (clang::Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I != E; ++I)
0170       if (clang::Stmt *child = *I) {
0171         Visit(child);
0172       }
0173   }
0174 
0175   void WalkAST::CheckBinaryOperator(const clang::BinaryOperator *BO, const clang::MemberExpr *IME) {
0176     if (BO->isAssignmentOp()) {
0177       if (clang::MemberExpr *ME = dyn_cast_or_null<clang::MemberExpr>(BO->getLHS())) {
0178         if (ME->isImplicitAccess())
0179           ReportMember(ME);
0180       }
0181     } else {
0182       if (clang::UnaryOperator *UO =
0183               llvm::dyn_cast_or_null<clang::UnaryOperator>(BO->getLHS()->IgnoreParenImpCasts())) {
0184         if (UO->getOpcode() == clang::UnaryOperatorKind::UO_Deref) {
0185           if (clang::MemberExpr *ME = dyn_cast_or_null<clang::MemberExpr>(UO->getSubExpr()->IgnoreParenImpCasts())) {
0186             if (ME->isImplicitAccess())
0187               ReportMember(ME);
0188           }
0189           if (clang::DeclRefExpr *DRE = dyn_cast_or_null<clang::DeclRefExpr>(UO->getSubExpr()->IgnoreParenImpCasts())) {
0190             if (const clang::VarDecl *D = llvm::dyn_cast_or_null<clang::VarDecl>(DRE->getDecl())) {
0191               clang::QualType t = D->getType();
0192               const clang::Expr *E = llvm::dyn_cast_or_null<clang::Expr>(D->getInit());
0193               if (E && t->isPointerType()) {
0194                 const clang::MemberExpr *ME = dyn_cast_or_null<clang::MemberExpr>(E->IgnoreParenImpCasts());
0195                 if (ME && ME->isImplicitAccess())
0196                   ReportMember(ME);
0197               }
0198             }
0199           }
0200         }
0201       }
0202     }
0203   }
0204 
0205   void WalkAST::CheckUnaryOperator(const clang::UnaryOperator *UO, const clang::MemberExpr *E) {
0206     if (UO->isIncrementDecrementOp()) {
0207       if (clang::MemberExpr *ME = dyn_cast_or_null<clang::MemberExpr>(UO->getSubExpr()->IgnoreParenImpCasts()))
0208         ReportMember(ME);
0209     }
0210   }
0211 
0212   void WalkAST::CheckCXXOperatorCallExpr(const clang::CXXOperatorCallExpr *OCE, const clang::MemberExpr *E) {
0213     switch (OCE->getOperator()) {
0214       case OO_Equal:
0215       case OO_PlusEqual:
0216       case OO_MinusEqual:
0217       case OO_StarEqual:
0218       case OO_SlashEqual:
0219       case OO_PercentEqual:
0220       case OO_AmpEqual:
0221       case OO_PipeEqual:
0222       case OO_LessLessEqual:
0223       case OO_GreaterGreaterEqual:
0224         if (const clang::MemberExpr *ME =
0225                 dyn_cast_or_null<clang::MemberExpr>((*OCE->arg_begin())->IgnoreParenImpCasts())) {
0226           if (ME->isImplicitAccess())
0227             ReportMember(ME);
0228         }
0229 
0230       case OO_PlusPlus:
0231       case OO_MinusMinus:
0232         if (const clang::MemberExpr *ME = dyn_cast_or_null<clang::MemberExpr>(OCE->getCallee()->IgnoreParenCasts())) {
0233           if (ME->isImplicitAccess())
0234             ReportMember(ME);
0235         }
0236 
0237       default:
0238         return;
0239     }
0240   }
0241 
0242   void WalkAST::CheckExplicitCastExpr(const clang::ExplicitCastExpr *CE, const clang::MemberExpr *ME) {
0243     if (!(clang::CStyleCastExpr::classof(CE) || clang::CXXConstCastExpr::classof(CE)))
0244       return;
0245     const clang::Expr *E = CE->getSubExpr();
0246     clang::ASTContext &Ctx = AC->getASTContext();
0247     clang::QualType OrigTy = Ctx.getCanonicalType(E->getType());
0248     clang::QualType ToTy = Ctx.getCanonicalType(CE->getType());
0249 
0250     if (support::isConst(OrigTy) && !support::isConst(ToTy))
0251       ReportCast(CE);
0252   }
0253 
0254   void WalkAST::CheckReturnStmt(const clang::ReturnStmt *RS, const clang::MemberExpr *E) {
0255     if (const clang::Expr *RE = RS->getRetValue()) {
0256       clang::ASTContext &Ctx = AC->getASTContext();
0257       const clang::CXXMethodDecl *MD;
0258       if (visitingCallExpr)
0259         MD = visitingCallExpr->getMethodDecl();
0260       else
0261         MD = llvm::dyn_cast<clang::CXXMethodDecl>(AD);
0262       if (llvm::isa<clang::CXXNewExpr>(RE))
0263         return;
0264       clang::QualType RQT = MD->getCallResultType();
0265       clang::QualType RTy = Ctx.getCanonicalType(RQT);
0266       if ((RTy->isPointerType() || RTy->isReferenceType())) {
0267         if (!support::isConst(RTy)) {
0268           ReportCallReturn(RS);
0269         }
0270       }
0271       std::string svname = "const class std::vector<";
0272       std::string rtname = RTy.getAsString();
0273       if ((RTy->isReferenceType() || RTy->isRecordType()) && support::isConst(RTy) &&
0274           rtname.substr(0, svname.length()) == svname) {
0275         const CXXRecordDecl *RD;
0276         if (RTy->isRecordType())
0277           RD = RTy->getAsCXXRecordDecl();
0278         else
0279           RD = RTy->getPointeeCXXRecordDecl();
0280         const ClassTemplateSpecializationDecl *SD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
0281         for (unsigned J = 0, F = SD->getTemplateArgs().size(); J != F; ++J) {
0282           if (SD->getTemplateArgs().get(J).getKind() == clang::TemplateArgument::Type) {
0283             const QualType QAT = SD->getTemplateArgs().get(J).getAsType();
0284             if (QAT->isPointerType() && !support::isConst(QAT)) {
0285               std::string buf;
0286               llvm::raw_string_ostream os(buf);
0287               std::string mname = support::getQualifiedName(*MD);
0288               support::fixAnonNS(mname, sfile);
0289               std::string pname = support::getQualifiedName(*(MD->getParent()));
0290               support::fixAnonNS(pname, sfile);
0291               os << mname << " is a const member function that returns a const std::vector<*> or const std::vector<*>& "
0292                  << rtname << "\n";
0293               std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0294               writeLog(tolog);
0295               ReportCallReturn(RS);
0296             }
0297           }
0298         }
0299       }
0300     }
0301   }
0302 
0303   void WalkAST::VisitCXXConstCastExpr(clang::CXXConstCastExpr *CCE) {
0304     clang::ento::PathDiagnosticLocation CELoc =
0305         clang::ento::PathDiagnosticLocation::createBegin(CCE, BR.getSourceManager(), AC);
0306     std::string buf;
0307     llvm::raw_string_ostream os(buf);
0308     os << "const_cast used\n";
0309     std::string pname = support::getQualifiedName(*(AD->getParent()));
0310     support::fixAnonNS(pname, sfile);
0311     std::string mname = support::getQualifiedName(*AD);
0312     support::fixAnonNS(mname, sfile);
0313     std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str() + ".";
0314     writeLog(tolog);
0315     BugType *BT = new BugType(Checker, "const_cast used in const function ", "Data Class Const Correctness");
0316     std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(tolog), CELoc);
0317     BR.emitReport(std::move(R));
0318     return;
0319   }
0320 
0321   void WalkAST::VisitDeclRefExpr(clang::DeclRefExpr *DRE) {
0322     if (clang::VarDecl *D = llvm::dyn_cast_or_null<clang::VarDecl>(DRE->getDecl())) {
0323       clang::SourceLocation SL = DRE->getBeginLoc();
0324       if (BR.getSourceManager().isInSystemHeader(SL) || BR.getSourceManager().isInExternCSystemHeader(SL))
0325         return;
0326       if (support::isSafeClassName(D->getCanonicalDecl()->getQualifiedNameAsString()))
0327         return;
0328       ReportDeclRef(DRE);
0329     }
0330   }
0331 
0332   void WalkAST::ReportDeclRef(const clang::DeclRefExpr *DRE) {
0333     if (const clang::VarDecl *D = llvm::dyn_cast_or_null<clang::VarDecl>(DRE->getDecl())) {
0334       clang::QualType t = D->getType();
0335       const clang::Stmt *PS = ParentStmt(DRE);
0336       clang::LangOptions LangOpts;
0337       LangOpts.CPlusPlus = true;
0338       clang::PrintingPolicy Policy(LangOpts);
0339 
0340       clang::ento::PathDiagnosticLocation CELoc =
0341           clang::ento::PathDiagnosticLocation::createBegin(DRE, BR.getSourceManager(), AC);
0342       if (support::isSafeClassName(t.getCanonicalType().getAsString()))
0343         return;
0344       if (D->hasAttr<CMSThreadGuardAttr>() || D->hasAttr<CMSThreadSafeAttr>() || D->hasAttr<CMSSaAllowAttr>())
0345         return;
0346       if (D->isStaticLocal() && D->getTSCSpec() != clang::ThreadStorageClassSpecifier::TSCS_thread_local &&
0347           !support::isConst(t)) {
0348         std::string buf;
0349         llvm::raw_string_ostream os(buf);
0350         os << "Non-const variable '" << support::getQualifiedName(*D)
0351            << "' is static local and accessed in statement '";
0352         PS->printPretty(os, nullptr, Policy);
0353         os << "'.\n";
0354         std::string pname = support::getQualifiedName(*(AD->getParent()));
0355         support::fixAnonNS(pname, sfile);
0356         std::string mname = support::getQualifiedName(*AD);
0357         support::fixAnonNS(mname, sfile);
0358         std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0359         writeLog(tolog);
0360         BugType *BT = new BugType(
0361             Checker, "ClassChecker : non-const static local variable accessed", "Data Class Const Correctness");
0362         std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc);
0363         BR.emitReport(std::move(R));
0364         return;
0365       }
0366 
0367       if (D->isStaticDataMember() && D->getTSCSpec() != clang::ThreadStorageClassSpecifier::TSCS_thread_local &&
0368           !support::isConst(t)) {
0369         std::string buf;
0370         llvm::raw_string_ostream os(buf);
0371         os << "Non-const variable '" << support::getQualifiedName(*D)
0372            << "' is static member data and accessed in statement '";
0373         PS->printPretty(os, nullptr, Policy);
0374         os << "'.\n";
0375         std::string pname = support::getQualifiedName(*(AD->getParent()));
0376         support::fixAnonNS(pname, sfile);
0377         std::string mname = support::getQualifiedName(*AD);
0378         support::fixAnonNS(mname, sfile);
0379         std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0380         writeLog(tolog);
0381         BugType *BT = new BugType(Checker, "Non-const static member variable accessed", "Data Class Const Correctness");
0382         std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc);
0383         BR.emitReport(std::move(R));
0384         return;
0385       }
0386 
0387       if ((D->getStorageClass() == clang::SC_Static) && !D->isStaticDataMember() && !D->isStaticLocal() &&
0388           !support::isConst(t)) {
0389         std::string buf;
0390         llvm::raw_string_ostream os(buf);
0391         os << "Non-const variable '" << support::getQualifiedName(*D)
0392            << "' is global static and accessed in statement '";
0393         PS->printPretty(os, nullptr, Policy);
0394         os << "'.\n";
0395         std::string pname = support::getQualifiedName(*(AD->getParent()));
0396         support::fixAnonNS(pname, sfile);
0397         std::string mname = support::getQualifiedName(*AD);
0398         support::fixAnonNS(mname, sfile);
0399         std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0400         writeLog(tolog);
0401         BugType *BT = new BugType(Checker, "Non-const global static variable accessed", "Data Class Const Correctness");
0402         std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc);
0403         BR.emitReport(std::move(R));
0404         return;
0405       }
0406     }
0407   }
0408 
0409   void WalkAST::VisitMemberExpr(clang::MemberExpr *ME) {
0410     clang::SourceLocation SL = ME->getExprLoc();
0411     if (BR.getSourceManager().isInSystemHeader(SL) || BR.getSourceManager().isInExternCSystemHeader(SL))
0412       return;
0413     const ValueDecl *D = ME->getMemberDecl();
0414     if (D->hasAttr<CMSThreadGuardAttr>() || D->hasAttr<CMSThreadSafeAttr>() || D->hasAttr<CMSSaAllowAttr>())
0415       return;
0416     if (!(ME->isImplicitAccess()))
0417       return;
0418     Stmt *P = AC->getParentMap().getParent(ME);
0419     while (AC->getParentMap().hasParent(P)) {
0420       if (const clang::UnaryOperator *UO = llvm::dyn_cast_or_null<clang::UnaryOperator>(P)) {
0421         WalkAST::CheckUnaryOperator(UO, ME);
0422       }
0423       if (const clang::BinaryOperator *BO = llvm::dyn_cast_or_null<clang::BinaryOperator>(P)) {
0424         WalkAST::CheckBinaryOperator(BO, ME);
0425       }
0426       if (const clang::CXXOperatorCallExpr *OCE = llvm::dyn_cast_or_null<clang::CXXOperatorCallExpr>(P)) {
0427         WalkAST::CheckCXXOperatorCallExpr(OCE, ME);
0428       }
0429       if (const clang::ExplicitCastExpr *CE = llvm::dyn_cast_or_null<clang::ExplicitCastExpr>(P)) {
0430         WalkAST::CheckExplicitCastExpr(CE, ME);
0431       }
0432       if (const clang::ReturnStmt *RS = llvm::dyn_cast_or_null<clang::ReturnStmt>(P)) {
0433         WalkAST::CheckReturnStmt(RS, ME);
0434       }
0435       if (const clang::CXXConstCastExpr *CCE = llvm::dyn_cast_or_null<clang::CXXConstCastExpr>(P)) {
0436         WalkAST::ReportCast(CCE);
0437       }
0438       const clang::CXXNewExpr *NE = llvm::dyn_cast_or_null<clang::CXXNewExpr>(P);
0439       if (NE)
0440         break;
0441       P = AC->getParentMap().getParent(P);
0442     }
0443   }
0444 
0445   void WalkAST::VisitCXXMemberCallExpr(clang::CXXMemberCallExpr *CE) {
0446     if (BR.getSourceManager().isInSystemHeader(CE->getExprLoc()) ||
0447         BR.getSourceManager().isInExternCSystemHeader(CE->getExprLoc()))
0448       return;
0449 
0450     clang::CXXMethodDecl *MD = CE->getMethodDecl();
0451 
0452     if (!MD)
0453       return;
0454 
0455     Enqueue(CE);
0456     Execute();
0457     Visit(CE->getImplicitObjectArgument()->IgnoreParenCasts());
0458 
0459     const Expr *IOA = CE->getImplicitObjectArgument()->IgnoreParenCasts();
0460     const MemberExpr *ME = dyn_cast_or_null<MemberExpr>(IOA);
0461     if (!MD->isConst() && ME && ME->isImplicitAccess())
0462       ReportCall(CE);
0463 
0464     for (int i = 0, j = CE->getNumArgs(); i < j; i++) {
0465       if (CE->getArg(i)) {
0466         if (const clang::Expr *E = llvm::dyn_cast_or_null<clang::Expr>(CE->getArg(i))) {
0467           const clang::MemberExpr *AME = llvm::dyn_cast_or_null<clang::MemberExpr>(E);
0468           if (AME && AME->isImplicitAccess()) {
0469             clang::ParmVarDecl *PVD = llvm::dyn_cast_or_null<clang::ParmVarDecl>(MD->getParamDecl(i));
0470             clang::QualType QT = PVD->getOriginalType();
0471             const clang::Type *T = QT.getTypePtr();
0472             if (!support::isConst(QT) && T->isReferenceType() && ME && ME->isImplicitAccess())
0473               ReportCallArg(CE, i);
0474           }
0475         }
0476       }
0477     }
0478   }
0479 
0480   void WalkAST::ReportMember(const clang::MemberExpr *ME) {
0481     const ValueDecl *D = ME->getMemberDecl();
0482     if (D->hasAttr<CMSThreadGuardAttr>() || D->hasAttr<CMSThreadSafeAttr>() || D->hasAttr<CMSSaAllowAttr>())
0483       return;
0484     if (visitingCallExpr) {
0485       clang::Expr *IOA = visitingCallExpr->getImplicitObjectArgument();
0486       if (!(IOA->isImplicitCXXThis() || llvm::dyn_cast_or_null<CXXThisExpr>(IOA->IgnoreParenCasts())))
0487         return;
0488     }
0489     std::string buf;
0490     llvm::raw_string_ostream os(buf);
0491     clang::ento::PathDiagnosticLocation CELoc;
0492     clang::SourceRange R;
0493     clang::LangOptions LangOpts;
0494     LangOpts.CPlusPlus = true;
0495     clang::PrintingPolicy Policy(LangOpts);
0496 
0497     CELoc = clang::ento::PathDiagnosticLocation::createBegin(ME, BR.getSourceManager(), AC);
0498     R = ME->getSourceRange();
0499 
0500     os << "Member data '";
0501     ME->printPretty(os, nullptr, Policy);
0502     os << "' is directly or indirectly modified in const function\n";
0503     std::string pname = support::getQualifiedName(*(AD->getParent()));
0504     support::fixAnonNS(pname, sfile);
0505     std::string mname = support::getQualifiedName(*AD);
0506     support::fixAnonNS(mname, sfile);
0507     std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0508     writeLog(tolog);
0509     BR.EmitBasicReport(
0510         AD, Checker, "Member data modified in const function", "Data Class Const Correctness", os.str(), CELoc);
0511   }
0512 
0513   void WalkAST::ReportCall(const clang::CXXMemberCallExpr *CE) {
0514     const clang::CXXRecordDecl *RD = CE->getRecordDecl();
0515     const clang::CXXMethodDecl *MD = CE->getMethodDecl();
0516     if (!RD || support::isSafeClassName(RD->getQualifiedNameAsString()))
0517       return;
0518     std::string buf;
0519     llvm::raw_string_ostream os(buf);
0520     clang::ento::PathDiagnosticLocation CELoc =
0521         clang::ento::PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
0522     clang::LangOptions LangOpts;
0523     LangOpts.CPlusPlus = true;
0524     clang::PrintingPolicy Policy(LangOpts);
0525     os << "call expr '";
0526     CE->printPretty(os, nullptr, Policy);
0527     os << "' with implicit object argument '";
0528     CE->getImplicitObjectArgument()->IgnoreParenCasts()->printPretty(os, nullptr, Policy);
0529     os << "'";
0530     os << "' is a non-const member function '" << support::getQualifiedName(*MD);
0531     os << "' that could modify member data object of type '" << support::getQualifiedName(*RD) << "'\n";
0532     std::string pname = support::getQualifiedName(*(AD->getParent()));
0533     support::fixAnonNS(pname, sfile);
0534     std::string mname = support::getQualifiedName(*AD);
0535     support::fixAnonNS(mname, sfile);
0536     std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0537     if (support::isSafeClassName(support::getQualifiedName(*MD)))
0538       return;
0539     writeLog(tolog);
0540     BugType *BT = new BugType(
0541         Checker, "Non-const member function could modify member data object", "Data Class Const Correctness");
0542     std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc);
0543     BR.emitReport(std::move(R));
0544   }
0545 
0546   void WalkAST::ReportCast(const clang::ExplicitCastExpr *CE) {
0547     std::string buf;
0548     llvm::raw_string_ostream os(buf);
0549 
0550     clang::LangOptions LangOpts;
0551     LangOpts.CPlusPlus = true;
0552     clang::PrintingPolicy Policy(LangOpts);
0553 
0554     os << "Const qualifier of member data object";
0555     os << " was removed via cast expression '";
0556     std::string pname = support::getQualifiedName(*(AD->getParent()));
0557     support::fixAnonNS(pname, sfile);
0558     std::string mname = support::getQualifiedName(*AD);
0559     support::fixAnonNS(mname, sfile);
0560     std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0561     clang::ento::PathDiagnosticLocation CELoc =
0562         clang::ento::PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
0563 
0564     writeLog(tolog);
0565     BugType *BT =
0566         new BugType(Checker, "Const cast away from member data in const function", "Data Class Const Correctness");
0567     std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc);
0568     BR.emitReport(std::move(R));
0569   }
0570 
0571   void WalkAST::ReportCallArg(const clang::CXXMemberCallExpr *CE, const int i) {
0572     std::string buf;
0573     llvm::raw_string_ostream os(buf);
0574     clang::LangOptions LangOpts;
0575     LangOpts.CPlusPlus = true;
0576     clang::PrintingPolicy Policy(LangOpts);
0577 
0578     clang::CXXMethodDecl *CMD = llvm::dyn_cast<clang::CXXMemberCallExpr>(CE)->getMethodDecl();
0579     const clang::MemberExpr *E = llvm::dyn_cast<clang::MemberExpr>(CE->getArg(i));
0580     clang::ValueDecl *VD = llvm::dyn_cast<clang::ValueDecl>(E->getMemberDecl());
0581     os << "Member data '" << VD->getQualifiedNameAsString();
0582     os << "' is passed to a non-const reference parameter";
0583     os << " of CXX method '" << CMD->getQualifiedNameAsString() << "' in const function";
0584     os << "\n";
0585 
0586     std::string pname = support::getQualifiedName(*(AD->getParent()));
0587     support::fixAnonNS(pname, sfile);
0588     std::string mname = support::getQualifiedName(*AD);
0589     support::fixAnonNS(mname, sfile);
0590 
0591     std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0592 
0593     clang::ento::PathDiagnosticLocation ELoc =
0594         clang::ento::PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
0595 
0596     writeLog(tolog);
0597     BR.EmitBasicReport(CE->getCalleeDecl(),
0598                        Checker,
0599                        "Member data passed to non-const reference",
0600                        "Data Class Const Correctness",
0601                        os.str(),
0602                        ELoc);
0603   }
0604 
0605   void WalkAST::ReportCallReturn(const clang::ReturnStmt *RS) {
0606     std::string buf;
0607     llvm::raw_string_ostream os(buf);
0608 
0609     clang::LangOptions LangOpts;
0610     LangOpts.CPlusPlus = true;
0611     clang::PrintingPolicy Policy(LangOpts);
0612 
0613     os << "Returns a pointer or reference to a non-const member data object ";
0614     os << " or a const std::vector<*> or const std::vector<*>& ";
0615     os << "in const function in statement '";
0616     RS->printPretty(os, nullptr, Policy);
0617     os << "\n";
0618     const clang::CXXMethodDecl *MD = llvm::cast<clang::CXXMethodDecl>(AD);
0619     clang::ento::PathDiagnosticLocation CELoc =
0620         clang::ento::PathDiagnosticLocation::createBegin(RS, BR.getSourceManager(), AC);
0621     std::string pname = support::getQualifiedName(*(AD->getParent()));
0622     support::fixAnonNS(pname, sfile);
0623     std::string mname = support::getQualifiedName(*AD);
0624     support::fixAnonNS(mname, sfile);
0625     std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0626     writeLog(tolog);
0627     clang::ASTContext &Ctx = AC->getASTContext();
0628     clang::QualType RQT = MD->getCallResultType();
0629     clang::QualType RTy = Ctx.getCanonicalType(RQT);
0630     if ((RTy->isPointerType() || RTy->isReferenceType())) {
0631       if (!support::isConst(RTy)) {
0632         BugType *BT = new BugType(Checker,
0633                                   "Const function returns pointer or reference to non-const member data object",
0634                                   "Data Class Const Correctness");
0635         std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc);
0636         BR.emitReport(std::move(R));
0637       }
0638     }
0639     std::string svname = "const class std::vector<";
0640     std::string rtname = RTy.getAsString();
0641     if ((RTy->isReferenceType() || RTy->isRecordType()) && support::isConst(RTy) &&
0642         rtname.substr(0, svname.length()) == svname) {
0643       BugType *BT =
0644           new BugType(Checker,
0645                       "Const function returns member data object of type const std::vector<*> or const std::vector<*>&",
0646                       "Data Class Const Correctness");
0647       std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc);
0648       BR.emitReport(std::move(R));
0649     }
0650   }
0651 
0652   void ClassChecker::checkASTDecl(const clang::CXXRecordDecl *RD,
0653                                   clang::ento::AnalysisManager &mgr,
0654                                   clang::ento::BugReporter &BR) const {
0655     const clang::SourceManager &SM = BR.getSourceManager();
0656     const char *sfile = SM.getPresumedLoc(RD->getLocation()).getFilename();
0657     if (!support::isCmsLocalFile(sfile))
0658       return;
0659     std::string buf;
0660     llvm::raw_string_ostream os(buf);
0661     std::string name = RD->getQualifiedNameAsString();
0662     if (!support::isDataClass(name))
0663       return;
0664     for (auto I = RD->field_begin(), E = RD->field_end(); I != E; ++I) {
0665       const FieldDecl *D = (*I);
0666       if (D->hasAttr<CMSThreadGuardAttr>() || D->hasAttr<CMSThreadSafeAttr>() || D->hasAttr<CMSSaAllowAttr>())
0667         return;
0668       if (D->isMutable()) {
0669         clang::QualType t = D->getType();
0670         clang::ento::PathDiagnosticLocation DLoc =
0671             clang::ento::PathDiagnosticLocation::createBegin(D, BR.getSourceManager());
0672         if (support::isSafeClassName(t.getCanonicalType().getAsString()))
0673           return;
0674         if (!support::isDataClass(support::getQualifiedName(*RD)))
0675           return;
0676         std::string buf;
0677         llvm::raw_string_ostream os(buf);
0678         os << "Mutable member '" << t.getCanonicalType().getAsString() << " " << *D << "' in data class '"
0679            << support::getQualifiedName(*RD) << "', might be thread-unsafe when accessing via a const handle.";
0680         BR.EmitBasicReport(D, this, "Mutable member in data class", "Data Class Const Correctness", os.str(), DLoc);
0681         std::string pname = support::getQualifiedName(*(RD));
0682         support::fixAnonNS(pname, sfile);
0683         std::string mname = support::getQualifiedName(*D);
0684         support::fixAnonNS(mname, sfile);
0685         std::string tolog = "data class '" + pname + "' mutable member '" + mname + "' Warning: " + os.str();
0686         writeLog(tolog);
0687       }
0688     }
0689 
0690     // Check the class methods (member methods).
0691     for (clang::CXXRecordDecl::method_iterator I = RD->method_begin(), E = RD->method_end(); I != E; ++I) {
0692       if (!llvm::isa<clang::CXXMethodDecl>((*I)))
0693         continue;
0694       if (!(*I)->isConst())
0695         continue;
0696       clang::CXXMethodDecl *MD = llvm::cast<clang::CXXMethodDecl>((*I)->getMostRecentDecl());
0697       if (MD->hasAttr<CMSThreadGuardAttr>() || MD->hasAttr<CMSThreadSafeAttr>() || MD->hasAttr<CMSSaAllowAttr>())
0698         continue;
0699       if (MD->hasBody()) {
0700         clang::Stmt *Body = MD->getBody();
0701         WalkAST walker(this, BR, mgr.getAnalysisDeclContext(MD), MD, sfile);
0702         walker.Visit(Body);
0703         clang::QualType RQT = MD->getCallResultType();
0704         clang::ASTContext &Ctx = BR.getContext();
0705         clang::QualType RTy = Ctx.getCanonicalType(RQT);
0706         clang::ento::PathDiagnosticLocation ELoc = clang::ento::PathDiagnosticLocation::createBegin(MD, SM);
0707         if ((RTy->isPointerType() || RTy->isReferenceType()) && (!support::isConst(RTy)) &&
0708             (support::getQualifiedName(*MD).find("clone") == std::string::npos)) {
0709           std::string buf;
0710           llvm::raw_string_ostream os(buf);
0711           os << MD->getQualifiedNameAsString()
0712              << " is a const member function that returns a pointer or reference to a non-const object \n";
0713           std::string pname = support::getQualifiedName(*(MD->getParent()));
0714           support::fixAnonNS(pname, sfile);
0715           std::string mname = support::getQualifiedName(*MD);
0716           support::fixAnonNS(mname, sfile);
0717           std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0718           writeLog(tolog);
0719           BR.EmitBasicReport(MD,
0720                              this,
0721                              "Const function returns pointer or reference to non-const object.",
0722                              "Data Class Const Correctness",
0723                              os.str(),
0724                              ELoc);
0725         }
0726         std::string svname = "const class std::vector<";
0727         std::string rtname = RTy.getAsString();
0728         if ((RTy->isReferenceType() || RTy->isRecordType()) && support::isConst(RTy) &&
0729             rtname.substr(0, svname.length()) == svname) {
0730           const CXXRecordDecl *RD;
0731           if (RTy->isRecordType())
0732             RD = RTy->getAsCXXRecordDecl();
0733           else
0734             RD = RTy->getPointeeCXXRecordDecl();
0735           const ClassTemplateSpecializationDecl *SD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
0736           for (unsigned J = 0, F = SD->getTemplateArgs().size(); J != F; ++J) {
0737             if (SD->getTemplateArgs().get(J).getKind() == clang::TemplateArgument::Type) {
0738               const QualType QAT = SD->getTemplateArgs().get(J).getAsType();
0739               if (QAT->isPointerType() && !support::isConst(QAT)) {
0740                 std::string buf;
0741                 llvm::raw_string_ostream os(buf);
0742                 std::string pname = support::getQualifiedName(*(MD->getParent()));
0743                 support::fixAnonNS(pname, sfile);
0744                 std::string mname = support::getQualifiedName(*MD);
0745                 support::fixAnonNS(mname, sfile);
0746                 os << mname
0747                    << " is a const member function that returns an object of type const std::vector<*> or const "
0748                       "std::vector<*>& "
0749                    << rtname << "\n";
0750                 std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0751                 writeLog(tolog);
0752                 BR.EmitBasicReport(MD,
0753                                    this,
0754                                    "Const function returns const std::vector<*> or const std::vector<*>&",
0755                                    "Data Class Const Correctness",
0756                                    os.str(),
0757                                    ELoc);
0758               }
0759             }
0760           }
0761         }
0762       }
0763     } /* end of methods loop */
0764 
0765   }  //end of class
0766 
0767 }  // namespace clangcms