Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-10-08 23:10:07

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       assert(MD);
0265       clang::QualType RQT = MD->getCallResultType();
0266       clang::QualType RTy = Ctx.getCanonicalType(RQT);
0267       if ((RTy->isPointerType() || RTy->isReferenceType())) {
0268         if (!support::isConst(RTy)) {
0269           ReportCallReturn(RS);
0270         }
0271       }
0272       std::string svname = "const class std::vector<";
0273       std::string rtname = RTy.getAsString();
0274       if ((RTy->isReferenceType() || RTy->isRecordType()) && support::isConst(RTy) &&
0275           rtname.substr(0, svname.length()) == svname) {
0276         const CXXRecordDecl *RD;
0277         if (RTy->isRecordType())
0278           RD = RTy->getAsCXXRecordDecl();
0279         else
0280           RD = RTy->getPointeeCXXRecordDecl();
0281         const ClassTemplateSpecializationDecl *SD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
0282         for (unsigned J = 0, F = SD->getTemplateArgs().size(); J != F; ++J) {
0283           if (SD->getTemplateArgs().get(J).getKind() == clang::TemplateArgument::Type) {
0284             const QualType QAT = SD->getTemplateArgs().get(J).getAsType();
0285             if (QAT->isPointerType() && !support::isConst(QAT)) {
0286               std::string buf;
0287               llvm::raw_string_ostream os(buf);
0288               std::string mname = support::getQualifiedName(*MD);
0289               support::fixAnonNS(mname, sfile);
0290               std::string pname = support::getQualifiedName(*(MD->getParent()));
0291               support::fixAnonNS(pname, sfile);
0292               os << mname << " is a const member function that returns a const std::vector<*> or const std::vector<*>& "
0293                  << rtname << "\n";
0294               std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0295               writeLog(tolog);
0296               ReportCallReturn(RS);
0297             }
0298           }
0299         }
0300       }
0301     }
0302   }
0303 
0304   void WalkAST::VisitCXXConstCastExpr(clang::CXXConstCastExpr *CCE) {
0305     clang::ento::PathDiagnosticLocation CELoc =
0306         clang::ento::PathDiagnosticLocation::createBegin(CCE, BR.getSourceManager(), AC);
0307     std::string buf;
0308     llvm::raw_string_ostream os(buf);
0309     os << "const_cast used\n";
0310     std::string pname = support::getQualifiedName(*(AD->getParent()));
0311     support::fixAnonNS(pname, sfile);
0312     std::string mname = support::getQualifiedName(*AD);
0313     support::fixAnonNS(mname, sfile);
0314     std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str() + ".";
0315     writeLog(tolog);
0316     BugType *BT = new BugType(Checker, "const_cast used in const function ", "Data Class Const Correctness");
0317     std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(tolog), CELoc);
0318     BR.emitReport(std::move(R));
0319     return;
0320   }
0321 
0322   void WalkAST::VisitDeclRefExpr(clang::DeclRefExpr *DRE) {
0323     if (clang::VarDecl *D = llvm::dyn_cast_or_null<clang::VarDecl>(DRE->getDecl())) {
0324       clang::SourceLocation SL = DRE->getBeginLoc();
0325       if (BR.getSourceManager().isInSystemHeader(SL) || BR.getSourceManager().isInExternCSystemHeader(SL))
0326         return;
0327       if (support::isSafeClassName(D->getCanonicalDecl()->getQualifiedNameAsString()))
0328         return;
0329       ReportDeclRef(DRE);
0330     }
0331   }
0332 
0333   void WalkAST::ReportDeclRef(const clang::DeclRefExpr *DRE) {
0334     if (const clang::VarDecl *D = llvm::dyn_cast_or_null<clang::VarDecl>(DRE->getDecl())) {
0335       clang::QualType t = D->getType();
0336       const clang::Stmt *PS = ParentStmt(DRE);
0337       clang::LangOptions LangOpts;
0338       LangOpts.CPlusPlus = true;
0339       clang::PrintingPolicy Policy(LangOpts);
0340 
0341       clang::ento::PathDiagnosticLocation CELoc =
0342           clang::ento::PathDiagnosticLocation::createBegin(DRE, BR.getSourceManager(), AC);
0343       if (support::isSafeClassName(t.getCanonicalType().getAsString()))
0344         return;
0345       if (D->hasAttr<CMSThreadGuardAttr>() || D->hasAttr<CMSThreadSafeAttr>() || D->hasAttr<CMSSaAllowAttr>())
0346         return;
0347       if (D->isStaticLocal() && D->getTSCSpec() != clang::ThreadStorageClassSpecifier::TSCS_thread_local &&
0348           !support::isConst(t)) {
0349         std::string buf;
0350         llvm::raw_string_ostream os(buf);
0351         os << "Non-const variable '" << support::getQualifiedName(*D)
0352            << "' is static local and accessed in statement '";
0353         PS->printPretty(os, nullptr, Policy);
0354         os << "'.\n";
0355         std::string pname = support::getQualifiedName(*(AD->getParent()));
0356         support::fixAnonNS(pname, sfile);
0357         std::string mname = support::getQualifiedName(*AD);
0358         support::fixAnonNS(mname, sfile);
0359         std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0360         writeLog(tolog);
0361         BugType *BT = new BugType(
0362             Checker, "ClassChecker : non-const static local variable accessed", "Data Class Const Correctness");
0363         std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc);
0364         BR.emitReport(std::move(R));
0365         return;
0366       }
0367 
0368       if (D->isStaticDataMember() && D->getTSCSpec() != clang::ThreadStorageClassSpecifier::TSCS_thread_local &&
0369           !support::isConst(t)) {
0370         std::string buf;
0371         llvm::raw_string_ostream os(buf);
0372         os << "Non-const variable '" << support::getQualifiedName(*D)
0373            << "' is static member data and accessed in statement '";
0374         PS->printPretty(os, nullptr, Policy);
0375         os << "'.\n";
0376         std::string pname = support::getQualifiedName(*(AD->getParent()));
0377         support::fixAnonNS(pname, sfile);
0378         std::string mname = support::getQualifiedName(*AD);
0379         support::fixAnonNS(mname, sfile);
0380         std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0381         writeLog(tolog);
0382         BugType *BT = new BugType(Checker, "Non-const static member variable accessed", "Data Class Const Correctness");
0383         std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc);
0384         BR.emitReport(std::move(R));
0385         return;
0386       }
0387 
0388       if ((D->getStorageClass() == clang::SC_Static) && !D->isStaticDataMember() && !D->isStaticLocal() &&
0389           !support::isConst(t)) {
0390         std::string buf;
0391         llvm::raw_string_ostream os(buf);
0392         os << "Non-const variable '" << support::getQualifiedName(*D)
0393            << "' is global static and accessed in statement '";
0394         PS->printPretty(os, nullptr, Policy);
0395         os << "'.\n";
0396         std::string pname = support::getQualifiedName(*(AD->getParent()));
0397         support::fixAnonNS(pname, sfile);
0398         std::string mname = support::getQualifiedName(*AD);
0399         support::fixAnonNS(mname, sfile);
0400         std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0401         writeLog(tolog);
0402         BugType *BT = new BugType(Checker, "Non-const global static variable accessed", "Data Class Const Correctness");
0403         std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc);
0404         BR.emitReport(std::move(R));
0405         return;
0406       }
0407     }
0408   }
0409 
0410   void WalkAST::VisitMemberExpr(clang::MemberExpr *ME) {
0411     clang::SourceLocation SL = ME->getExprLoc();
0412     if (BR.getSourceManager().isInSystemHeader(SL) || BR.getSourceManager().isInExternCSystemHeader(SL))
0413       return;
0414     const ValueDecl *D = ME->getMemberDecl();
0415     if (D->hasAttr<CMSThreadGuardAttr>() || D->hasAttr<CMSThreadSafeAttr>() || D->hasAttr<CMSSaAllowAttr>())
0416       return;
0417     if (!(ME->isImplicitAccess()))
0418       return;
0419     Stmt *P = AC->getParentMap().getParent(ME);
0420     while (AC->getParentMap().hasParent(P)) {
0421       if (const clang::UnaryOperator *UO = llvm::dyn_cast_or_null<clang::UnaryOperator>(P)) {
0422         WalkAST::CheckUnaryOperator(UO, ME);
0423       }
0424       if (const clang::BinaryOperator *BO = llvm::dyn_cast_or_null<clang::BinaryOperator>(P)) {
0425         WalkAST::CheckBinaryOperator(BO, ME);
0426       }
0427       if (const clang::CXXOperatorCallExpr *OCE = llvm::dyn_cast_or_null<clang::CXXOperatorCallExpr>(P)) {
0428         WalkAST::CheckCXXOperatorCallExpr(OCE, ME);
0429       }
0430       if (const clang::ExplicitCastExpr *CE = llvm::dyn_cast_or_null<clang::ExplicitCastExpr>(P)) {
0431         WalkAST::CheckExplicitCastExpr(CE, ME);
0432       }
0433       if (const clang::ReturnStmt *RS = llvm::dyn_cast_or_null<clang::ReturnStmt>(P)) {
0434         WalkAST::CheckReturnStmt(RS, ME);
0435       }
0436       if (const clang::CXXConstCastExpr *CCE = llvm::dyn_cast_or_null<clang::CXXConstCastExpr>(P)) {
0437         WalkAST::ReportCast(CCE);
0438       }
0439       const clang::CXXNewExpr *NE = llvm::dyn_cast_or_null<clang::CXXNewExpr>(P);
0440       if (NE)
0441         break;
0442       P = AC->getParentMap().getParent(P);
0443     }
0444   }
0445 
0446   void WalkAST::VisitCXXMemberCallExpr(clang::CXXMemberCallExpr *CE) {
0447     if (BR.getSourceManager().isInSystemHeader(CE->getExprLoc()) ||
0448         BR.getSourceManager().isInExternCSystemHeader(CE->getExprLoc()))
0449       return;
0450 
0451     clang::CXXMethodDecl *MD = CE->getMethodDecl();
0452 
0453     if (!MD)
0454       return;
0455 
0456     Enqueue(CE);
0457     Execute();
0458     Visit(CE->getImplicitObjectArgument()->IgnoreParenCasts());
0459 
0460     const Expr *IOA = CE->getImplicitObjectArgument()->IgnoreParenCasts();
0461     const MemberExpr *ME = dyn_cast_or_null<MemberExpr>(IOA);
0462     if (!MD->isConst() && ME && ME->isImplicitAccess())
0463       ReportCall(CE);
0464 
0465     for (int i = 0, j = CE->getNumArgs(); i < j; i++) {
0466       if (CE->getArg(i)) {
0467         if (const clang::Expr *E = llvm::dyn_cast_or_null<clang::Expr>(CE->getArg(i))) {
0468           const clang::MemberExpr *AME = llvm::dyn_cast_or_null<clang::MemberExpr>(E);
0469           if (AME && AME->isImplicitAccess()) {
0470             clang::ParmVarDecl *PVD = llvm::dyn_cast_or_null<clang::ParmVarDecl>(MD->getParamDecl(i));
0471             assert(PVD);
0472             clang::QualType QT = PVD->getOriginalType();
0473             const clang::Type *T = QT.getTypePtr();
0474             if (!support::isConst(QT) && T->isReferenceType() && ME && ME->isImplicitAccess())
0475               ReportCallArg(CE, i);
0476           }
0477         }
0478       }
0479     }
0480   }
0481 
0482   void WalkAST::ReportMember(const clang::MemberExpr *ME) {
0483     const ValueDecl *D = ME->getMemberDecl();
0484     if (D->hasAttr<CMSThreadGuardAttr>() || D->hasAttr<CMSThreadSafeAttr>() || D->hasAttr<CMSSaAllowAttr>())
0485       return;
0486     if (visitingCallExpr) {
0487       clang::Expr *IOA = visitingCallExpr->getImplicitObjectArgument();
0488       if (!(IOA->isImplicitCXXThis() || llvm::dyn_cast_or_null<CXXThisExpr>(IOA->IgnoreParenCasts())))
0489         return;
0490     }
0491     std::string buf;
0492     llvm::raw_string_ostream os(buf);
0493     clang::ento::PathDiagnosticLocation CELoc;
0494     clang::SourceRange R;
0495     clang::LangOptions LangOpts;
0496     LangOpts.CPlusPlus = true;
0497     clang::PrintingPolicy Policy(LangOpts);
0498 
0499     CELoc = clang::ento::PathDiagnosticLocation::createBegin(ME, BR.getSourceManager(), AC);
0500     R = ME->getSourceRange();
0501 
0502     os << "Member data '";
0503     ME->printPretty(os, nullptr, Policy);
0504     os << "' is directly or indirectly modified in const function\n";
0505     std::string pname = support::getQualifiedName(*(AD->getParent()));
0506     support::fixAnonNS(pname, sfile);
0507     std::string mname = support::getQualifiedName(*AD);
0508     support::fixAnonNS(mname, sfile);
0509     std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0510     writeLog(tolog);
0511     BR.EmitBasicReport(
0512         AD, Checker, "Member data modified in const function", "Data Class Const Correctness", os.str(), CELoc);
0513   }
0514 
0515   void WalkAST::ReportCall(const clang::CXXMemberCallExpr *CE) {
0516     const clang::CXXRecordDecl *RD = CE->getRecordDecl();
0517     const clang::CXXMethodDecl *MD = CE->getMethodDecl();
0518     if (!RD || support::isSafeClassName(RD->getQualifiedNameAsString()))
0519       return;
0520     std::string buf;
0521     llvm::raw_string_ostream os(buf);
0522     clang::ento::PathDiagnosticLocation CELoc =
0523         clang::ento::PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
0524     clang::LangOptions LangOpts;
0525     LangOpts.CPlusPlus = true;
0526     clang::PrintingPolicy Policy(LangOpts);
0527     os << "call expr '";
0528     CE->printPretty(os, nullptr, Policy);
0529     os << "' with implicit object argument '";
0530     CE->getImplicitObjectArgument()->IgnoreParenCasts()->printPretty(os, nullptr, Policy);
0531     os << "'";
0532     os << "' is a non-const member function '" << support::getQualifiedName(*MD);
0533     os << "' that could modify member data object of type '" << support::getQualifiedName(*RD) << "'\n";
0534     std::string pname = support::getQualifiedName(*(AD->getParent()));
0535     support::fixAnonNS(pname, sfile);
0536     std::string mname = support::getQualifiedName(*AD);
0537     support::fixAnonNS(mname, sfile);
0538     std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0539     if (support::isSafeClassName(support::getQualifiedName(*MD)))
0540       return;
0541     writeLog(tolog);
0542     BugType *BT = new BugType(
0543         Checker, "Non-const member function could modify member data object", "Data Class Const Correctness");
0544     std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc);
0545     BR.emitReport(std::move(R));
0546   }
0547 
0548   void WalkAST::ReportCast(const clang::ExplicitCastExpr *CE) {
0549     std::string buf;
0550     llvm::raw_string_ostream os(buf);
0551 
0552     clang::LangOptions LangOpts;
0553     LangOpts.CPlusPlus = true;
0554     clang::PrintingPolicy Policy(LangOpts);
0555 
0556     os << "Const qualifier of member data object";
0557     os << " was removed via cast expression '";
0558     std::string pname = support::getQualifiedName(*(AD->getParent()));
0559     support::fixAnonNS(pname, sfile);
0560     std::string mname = support::getQualifiedName(*AD);
0561     support::fixAnonNS(mname, sfile);
0562     std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0563     clang::ento::PathDiagnosticLocation CELoc =
0564         clang::ento::PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
0565 
0566     writeLog(tolog);
0567     BugType *BT =
0568         new BugType(Checker, "Const cast away from member data in const function", "Data Class Const Correctness");
0569     std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc);
0570     BR.emitReport(std::move(R));
0571   }
0572 
0573   void WalkAST::ReportCallArg(const clang::CXXMemberCallExpr *CE, const int i) {
0574     std::string buf;
0575     llvm::raw_string_ostream os(buf);
0576     clang::LangOptions LangOpts;
0577     LangOpts.CPlusPlus = true;
0578     clang::PrintingPolicy Policy(LangOpts);
0579 
0580     assert(llvm::dyn_cast<clang::CXXMemberCallExpr>(CE));
0581     clang::CXXMethodDecl *CMD = llvm::dyn_cast<clang::CXXMemberCallExpr>(CE)->getMethodDecl();
0582     const clang::MemberExpr *E = llvm::dyn_cast<clang::MemberExpr>(CE->getArg(i));
0583     assert(E);
0584     clang::ValueDecl *VD = llvm::dyn_cast<clang::ValueDecl>(E->getMemberDecl());
0585     assert(VD);
0586     os << "Member data '" << VD->getQualifiedNameAsString();
0587     os << "' is passed to a non-const reference parameter";
0588     os << " of CXX method '" << CMD->getQualifiedNameAsString() << "' in const function";
0589     os << "\n";
0590 
0591     std::string pname = support::getQualifiedName(*(AD->getParent()));
0592     support::fixAnonNS(pname, sfile);
0593     std::string mname = support::getQualifiedName(*AD);
0594     support::fixAnonNS(mname, sfile);
0595 
0596     std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0597 
0598     clang::ento::PathDiagnosticLocation ELoc =
0599         clang::ento::PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
0600 
0601     writeLog(tolog);
0602     BR.EmitBasicReport(CE->getCalleeDecl(),
0603                        Checker,
0604                        "Member data passed to non-const reference",
0605                        "Data Class Const Correctness",
0606                        os.str(),
0607                        ELoc);
0608   }
0609 
0610   void WalkAST::ReportCallReturn(const clang::ReturnStmt *RS) {
0611     std::string buf;
0612     llvm::raw_string_ostream os(buf);
0613 
0614     clang::LangOptions LangOpts;
0615     LangOpts.CPlusPlus = true;
0616     clang::PrintingPolicy Policy(LangOpts);
0617 
0618     os << "Returns a pointer or reference to a non-const member data object ";
0619     os << " or a const std::vector<*> or const std::vector<*>& ";
0620     os << "in const function in statement '";
0621     RS->printPretty(os, nullptr, Policy);
0622     os << "\n";
0623     const clang::CXXMethodDecl *MD = llvm::cast<clang::CXXMethodDecl>(AD);
0624     clang::ento::PathDiagnosticLocation CELoc =
0625         clang::ento::PathDiagnosticLocation::createBegin(RS, BR.getSourceManager(), AC);
0626     std::string pname = support::getQualifiedName(*(AD->getParent()));
0627     support::fixAnonNS(pname, sfile);
0628     std::string mname = support::getQualifiedName(*AD);
0629     support::fixAnonNS(mname, sfile);
0630     std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0631     writeLog(tolog);
0632     clang::ASTContext &Ctx = AC->getASTContext();
0633     clang::QualType RQT = MD->getCallResultType();
0634     clang::QualType RTy = Ctx.getCanonicalType(RQT);
0635     if ((RTy->isPointerType() || RTy->isReferenceType())) {
0636       if (!support::isConst(RTy)) {
0637         BugType *BT = new BugType(Checker,
0638                                   "Const function returns pointer or reference to non-const member data object",
0639                                   "Data Class Const Correctness");
0640         std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc);
0641         BR.emitReport(std::move(R));
0642       }
0643     }
0644     std::string svname = "const class std::vector<";
0645     std::string rtname = RTy.getAsString();
0646     if ((RTy->isReferenceType() || RTy->isRecordType()) && support::isConst(RTy) &&
0647         rtname.substr(0, svname.length()) == svname) {
0648       BugType *BT =
0649           new BugType(Checker,
0650                       "Const function returns member data object of type const std::vector<*> or const std::vector<*>&",
0651                       "Data Class Const Correctness");
0652       std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc);
0653       BR.emitReport(std::move(R));
0654     }
0655   }
0656 
0657   void ClassChecker::checkASTDecl(const clang::CXXRecordDecl *RD,
0658                                   clang::ento::AnalysisManager &mgr,
0659                                   clang::ento::BugReporter &BR) const {
0660     const clang::SourceManager &SM = BR.getSourceManager();
0661     const char *sfile = SM.getPresumedLoc(RD->getLocation()).getFilename();
0662     if (!support::isCmsLocalFile(sfile))
0663       return;
0664     std::string buf;
0665     llvm::raw_string_ostream os(buf);
0666     std::string name = RD->getQualifiedNameAsString();
0667     if (!support::isDataClass(name))
0668       return;
0669     for (auto I = RD->field_begin(), E = RD->field_end(); I != E; ++I) {
0670       const FieldDecl *D = (*I);
0671       if (D->hasAttr<CMSThreadGuardAttr>() || D->hasAttr<CMSThreadSafeAttr>() || D->hasAttr<CMSSaAllowAttr>())
0672         return;
0673       if (D->isMutable()) {
0674         clang::QualType t = D->getType();
0675         clang::ento::PathDiagnosticLocation DLoc =
0676             clang::ento::PathDiagnosticLocation::createBegin(D, BR.getSourceManager());
0677         if (support::isSafeClassName(t.getCanonicalType().getAsString()))
0678           return;
0679         if (!support::isDataClass(support::getQualifiedName(*RD)))
0680           return;
0681         std::string buf;
0682         llvm::raw_string_ostream os(buf);
0683         os << "Mutable member '" << t.getCanonicalType().getAsString() << " " << *D << "' in data class '"
0684            << support::getQualifiedName(*RD) << "', might be thread-unsafe when accessing via a const handle.";
0685         BR.EmitBasicReport(D, this, "Mutable member in data class", "Data Class Const Correctness", os.str(), DLoc);
0686         std::string pname = support::getQualifiedName(*(RD));
0687         support::fixAnonNS(pname, sfile);
0688         std::string mname = support::getQualifiedName(*D);
0689         support::fixAnonNS(mname, sfile);
0690         std::string tolog = "data class '" + pname + "' mutable member '" + mname + "' Warning: " + os.str();
0691         writeLog(tolog);
0692       }
0693     }
0694 
0695     // Check the class methods (member methods).
0696     for (clang::CXXRecordDecl::method_iterator I = RD->method_begin(), E = RD->method_end(); I != E; ++I) {
0697       if (!llvm::isa<clang::CXXMethodDecl>((*I)))
0698         continue;
0699       if (!(*I)->isConst())
0700         continue;
0701       clang::CXXMethodDecl *MD = llvm::cast<clang::CXXMethodDecl>((*I)->getMostRecentDecl());
0702       if (MD->hasAttr<CMSThreadGuardAttr>() || MD->hasAttr<CMSThreadSafeAttr>() || MD->hasAttr<CMSSaAllowAttr>())
0703         continue;
0704       if (MD->hasBody()) {
0705         clang::Stmt *Body = MD->getBody();
0706         WalkAST walker(this, BR, mgr.getAnalysisDeclContext(MD), MD, sfile);
0707         walker.Visit(Body);
0708         clang::QualType RQT = MD->getCallResultType();
0709         clang::ASTContext &Ctx = BR.getContext();
0710         clang::QualType RTy = Ctx.getCanonicalType(RQT);
0711         clang::ento::PathDiagnosticLocation ELoc = clang::ento::PathDiagnosticLocation::createBegin(MD, SM);
0712         if ((RTy->isPointerType() || RTy->isReferenceType()) && (!support::isConst(RTy)) &&
0713             (support::getQualifiedName(*MD).find("clone") == std::string::npos)) {
0714           std::string buf;
0715           llvm::raw_string_ostream os(buf);
0716           os << MD->getQualifiedNameAsString()
0717              << " is a const member function that returns a pointer or reference to a non-const object \n";
0718           std::string pname = support::getQualifiedName(*(MD->getParent()));
0719           support::fixAnonNS(pname, sfile);
0720           std::string mname = support::getQualifiedName(*MD);
0721           support::fixAnonNS(mname, sfile);
0722           std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0723           writeLog(tolog);
0724           BR.EmitBasicReport(MD,
0725                              this,
0726                              "Const function returns pointer or reference to non-const object.",
0727                              "Data Class Const Correctness",
0728                              os.str(),
0729                              ELoc);
0730         }
0731         std::string svname = "const class std::vector<";
0732         std::string rtname = RTy.getAsString();
0733         if ((RTy->isReferenceType() || RTy->isRecordType()) && support::isConst(RTy) &&
0734             rtname.substr(0, svname.length()) == svname) {
0735           const CXXRecordDecl *RD;
0736           if (RTy->isRecordType())
0737             RD = RTy->getAsCXXRecordDecl();
0738           else
0739             RD = RTy->getPointeeCXXRecordDecl();
0740           const ClassTemplateSpecializationDecl *SD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
0741           for (unsigned J = 0, F = SD->getTemplateArgs().size(); J != F; ++J) {
0742             if (SD->getTemplateArgs().get(J).getKind() == clang::TemplateArgument::Type) {
0743               const QualType QAT = SD->getTemplateArgs().get(J).getAsType();
0744               if (QAT->isPointerType() && !support::isConst(QAT)) {
0745                 std::string buf;
0746                 llvm::raw_string_ostream os(buf);
0747                 std::string pname = support::getQualifiedName(*(MD->getParent()));
0748                 support::fixAnonNS(pname, sfile);
0749                 std::string mname = support::getQualifiedName(*MD);
0750                 support::fixAnonNS(mname, sfile);
0751                 os << mname
0752                    << " is a const member function that returns an object of type const std::vector<*> or const "
0753                       "std::vector<*>& "
0754                    << rtname << "\n";
0755                 std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str();
0756                 writeLog(tolog);
0757                 BR.EmitBasicReport(MD,
0758                                    this,
0759                                    "Const function returns const std::vector<*> or const std::vector<*>&",
0760                                    "Data Class Const Correctness",
0761                                    os.str(),
0762                                    ELoc);
0763               }
0764             }
0765           }
0766         }
0767       }
0768     } /* end of methods loop */
0769 
0770   }  //end of class
0771 
0772 }  // namespace clangcms