/
githubmirror
/
cmssw
Обзор
Документация
Войти
/
githubmirror
/
cmssw
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
Utilities/StaticAnalyzers/src/ClassChecker.cpp
772 строки
33 KB
Shahzad Malik Muzaffar
[CORE] [Clang]Cleanup clang-analyzer warnings
04 окт 2024, 15:46
04 окт 2024, 15:46
401976a
Код
Авторство
О чём код?
// ClassChecker.cpp by Patrick Gartung (gartung@fnal.gov) // // Objectives of this checker // // For each special function of a class (produce, beginrun, endrun, beginlumi, endlumi) // // 1) identify member data being modified // built-in types reseting values // calling non-const member function object if member data is an object // 2) for each non-const member functions of self called // do 1) above // 3) for each non member function (external) passed in a member object // complain if arguement passed in by non-const ref // pass by value OK // pass by const ref & pointer OK // // #include <clang/AST/Decl.h> #include <clang/AST/Attr.h> #include <clang/AST/DeclTemplate.h> #include <clang/AST/DeclCXX.h> #include <clang/AST/StmtVisitor.h> #include <clang/AST/ParentMap.h> #include <clang/Analysis/CFGStmtMap.h> #include <clang/StaticAnalyzer/Core/Checker.h> #include <clang/StaticAnalyzer/Core/BugReporter/BugReporter.h> #include <clang/StaticAnalyzer/Core/BugReporter/BugType.h> #include <clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h> #include <llvm/Support/SaveAndRestore.h> #include <llvm/ADT/SmallString.h> #include "ClassChecker.h" #include <iostream> #include <fstream> #include <iterator> #include <string> #include <algorithm> using namespace clang; using namespace clang::ento; using namespace llvm; namespace clangcms { void writeLog(std::string ostring) { std::string tname = "class-checker.txt.unsorted"; support::writeLog(ostring, tname); return; } class WalkAST : public clang::StmtVisitor<WalkAST> { const CheckerBase *Checker; clang::ento::BugReporter &BR; clang::AnalysisDeclContext *AC; const CXXMethodDecl *AD; typedef const clang::CXXMemberCallExpr *WorkListUnit; typedef clang::SmallVector<WorkListUnit, 50> DFSWorkList; /// A vector representing the worklist which has a chain of CallExprs. DFSWorkList WList; // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the // body has not been visited yet. // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the // body has been visited. enum Kind { NotVisited, Visiting, /**< A CallExpr to this FunctionDecl is in the worklist, but the body has not yet been visited. */ Visited /**< A CallExpr to this FunctionDecl is in the worklist, and the body has been visited. */ }; /// A DenseMap that records visited states of FunctionDecls. llvm::DenseMap<const clang::CXXMemberCallExpr *, Kind> VisitedFunctions; /// The CallExpr whose body is currently being visited. This is used for /// generating bug reports. This is null while visiting the body of a /// constructor or destructor. const clang::CXXMemberCallExpr *visitingCallExpr; const char *sfile; public: WalkAST(const CheckerBase *checker, clang::ento::BugReporter &br, clang::AnalysisDeclContext *ac, const CXXMethodDecl *fd, const char *file) : Checker(checker), BR(br), AC(ac), AD(fd), visitingCallExpr(nullptr), sfile(file) {} bool hasWork() const { return !WList.empty(); } /// This method adds a CallExpr to the worklist void Enqueue(WorkListUnit WLUnit) { Kind &K = VisitedFunctions[WLUnit]; if ((K = Visiting)) return; K = Visiting; WList.push_back(WLUnit); } /// This method returns an item from the worklist without removing it. WorkListUnit Dequeue() { assert(!WList.empty()); return WList.back(); } void Execute() { if (WList.empty()) return; WorkListUnit WLUnit = Dequeue(); const clang::CXXMethodDecl *FD = WLUnit->getMethodDecl(); if (!FD) return; llvm::SaveAndRestore<const clang::CXXMemberCallExpr *> SaveCall(visitingCallExpr, WLUnit); if (FD && FD->hasBody()) Visit(FD->getBody()); VisitedFunctions[WLUnit] = Visited; WList.pop_back(); } const clang::Stmt *ParentStmt(const Stmt *S) { const Stmt *P = AC->getParentMap().getParentIgnoreParens(S); if (!P) return nullptr; return P; } void WListDump(llvm::raw_ostream &os) { clang::LangOptions LangOpts; LangOpts.CPlusPlus = true; clang::PrintingPolicy Policy(LangOpts); if (!WList.empty()) { for (llvm::SmallVectorImpl<const clang::CXXMemberCallExpr *>::iterator I = WList.begin(), E = WList.end(); I != E; I++) { (*I)->printPretty(os, nullptr, Policy); os << " "; } } } // Stmt visitor methods. void VisitChildren(clang::Stmt *S); void VisitStmt(clang::Stmt *S) { VisitChildren(S); } void VisitMemberExpr(clang::MemberExpr *E); void VisitCXXMemberCallExpr(clang::CXXMemberCallExpr *CE); void VisitDeclRefExpr(clang::DeclRefExpr *DRE); void VisitCXXConstCastExpr(clang::CXXConstCastExpr *CCE); void ReportDeclRef(const clang::DeclRefExpr *DRE); void CheckCXXOperatorCallExpr(const clang::CXXOperatorCallExpr *CE, const clang::MemberExpr *E); void CheckBinaryOperator(const clang::BinaryOperator *BO, const clang::MemberExpr *E); void CheckUnaryOperator(const clang::UnaryOperator *UO, const clang::MemberExpr *E); void CheckExplicitCastExpr(const clang::ExplicitCastExpr *CE, const clang::MemberExpr *E); void CheckReturnStmt(const clang::ReturnStmt *RS, const clang::MemberExpr *E); void ReportCast(const clang::ExplicitCastExpr *CE); void ReportCall(const clang::CXXMemberCallExpr *CE); void ReportMember(const clang::MemberExpr *ME); void ReportCallReturn(const clang::ReturnStmt *RS); void ReportCallArg(const clang::CXXMemberCallExpr *CE, const int i); }; //===----------------------------------------------------------------------===// // AST walking. //===----------------------------------------------------------------------===// void WalkAST::VisitChildren(clang::Stmt *S) { for (clang::Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I != E; ++I) if (clang::Stmt *child = *I) { Visit(child); } } void WalkAST::CheckBinaryOperator(const clang::BinaryOperator *BO, const clang::MemberExpr *IME) { if (BO->isAssignmentOp()) { if (clang::MemberExpr *ME = dyn_cast_or_null<clang::MemberExpr>(BO->getLHS())) { if (ME->isImplicitAccess()) ReportMember(ME); } } else { if (clang::UnaryOperator *UO = llvm::dyn_cast_or_null<clang::UnaryOperator>(BO->getLHS()->IgnoreParenImpCasts())) { if (UO->getOpcode() == clang::UnaryOperatorKind::UO_Deref) { if (clang::MemberExpr *ME = dyn_cast_or_null<clang::MemberExpr>(UO->getSubExpr()->IgnoreParenImpCasts())) { if (ME->isImplicitAccess()) ReportMember(ME); } if (clang::DeclRefExpr *DRE = dyn_cast_or_null<clang::DeclRefExpr>(UO->getSubExpr()->IgnoreParenImpCasts())) { if (const clang::VarDecl *D = llvm::dyn_cast_or_null<clang::VarDecl>(DRE->getDecl())) { clang::QualType t = D->getType(); const clang::Expr *E = llvm::dyn_cast_or_null<clang::Expr>(D->getInit()); if (E && t->isPointerType()) { const clang::MemberExpr *ME = dyn_cast_or_null<clang::MemberExpr>(E->IgnoreParenImpCasts()); if (ME && ME->isImplicitAccess()) ReportMember(ME); } } } } } } } void WalkAST::CheckUnaryOperator(const clang::UnaryOperator *UO, const clang::MemberExpr *E) { if (UO->isIncrementDecrementOp()) { if (clang::MemberExpr *ME = dyn_cast_or_null<clang::MemberExpr>(UO->getSubExpr()->IgnoreParenImpCasts())) ReportMember(ME); } } void WalkAST::CheckCXXOperatorCallExpr(const clang::CXXOperatorCallExpr *OCE, const clang::MemberExpr *E) { switch (OCE->getOperator()) { case OO_Equal: case OO_PlusEqual: case OO_MinusEqual: case OO_StarEqual: case OO_SlashEqual: case OO_PercentEqual: case OO_AmpEqual: case OO_PipeEqual: case OO_LessLessEqual: case OO_GreaterGreaterEqual: if (const clang::MemberExpr *ME = dyn_cast_or_null<clang::MemberExpr>((*OCE->arg_begin())->IgnoreParenImpCasts())) { if (ME->isImplicitAccess()) ReportMember(ME); } case OO_PlusPlus: case OO_MinusMinus: if (const clang::MemberExpr *ME = dyn_cast_or_null<clang::MemberExpr>(OCE->getCallee()->IgnoreParenCasts())) { if (ME->isImplicitAccess()) ReportMember(ME); } default: return; } } void WalkAST::CheckExplicitCastExpr(const clang::ExplicitCastExpr *CE, const clang::MemberExpr *ME) { if (!(clang::CStyleCastExpr::classof(CE) || clang::CXXConstCastExpr::classof(CE))) return; const clang::Expr *E = CE->getSubExpr(); clang::ASTContext &Ctx = AC->getASTContext(); clang::QualType OrigTy = Ctx.getCanonicalType(E->getType()); clang::QualType ToTy = Ctx.getCanonicalType(CE->getType()); if (support::isConst(OrigTy) && !support::isConst(ToTy)) ReportCast(CE); } void WalkAST::CheckReturnStmt(const clang::ReturnStmt *RS, const clang::MemberExpr *E) { if (const clang::Expr *RE = RS->getRetValue()) { clang::ASTContext &Ctx = AC->getASTContext(); const clang::CXXMethodDecl *MD; if (visitingCallExpr) MD = visitingCallExpr->getMethodDecl(); else MD = llvm::dyn_cast<clang::CXXMethodDecl>(AD); if (llvm::isa<clang::CXXNewExpr>(RE)) return; assert(MD); clang::QualType RQT = MD->getCallResultType(); clang::QualType RTy = Ctx.getCanonicalType(RQT); if ((RTy->isPointerType() || RTy->isReferenceType())) { if (!support::isConst(RTy)) { ReportCallReturn(RS); } } std::string svname = "const class std::vector<"; std::string rtname = RTy.getAsString(); if ((RTy->isReferenceType() || RTy->isRecordType()) && support::isConst(RTy) && rtname.substr(0, svname.length()) == svname) { const CXXRecordDecl *RD; if (RTy->isRecordType()) RD = RTy->getAsCXXRecordDecl(); else RD = RTy->getPointeeCXXRecordDecl(); const ClassTemplateSpecializationDecl *SD = dyn_cast<ClassTemplateSpecializationDecl>(RD); for (unsigned J = 0, F = SD->getTemplateArgs().size(); J != F; ++J) { if (SD->getTemplateArgs().get(J).getKind() == clang::TemplateArgument::Type) { const QualType QAT = SD->getTemplateArgs().get(J).getAsType(); if (QAT->isPointerType() && !support::isConst(QAT)) { std::string buf; llvm::raw_string_ostream os(buf); std::string mname = support::getQualifiedName(*MD); support::fixAnonNS(mname, sfile); std::string pname = support::getQualifiedName(*(MD->getParent())); support::fixAnonNS(pname, sfile); os << mname << " is a const member function that returns a const std::vector<*> or const std::vector<*>& " << rtname << "\n"; std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str(); writeLog(tolog); ReportCallReturn(RS); } } } } } } void WalkAST::VisitCXXConstCastExpr(clang::CXXConstCastExpr *CCE) { clang::ento::PathDiagnosticLocation CELoc = clang::ento::PathDiagnosticLocation::createBegin(CCE, BR.getSourceManager(), AC); std::string buf; llvm::raw_string_ostream os(buf); os << "const_cast used\n"; std::string pname = support::getQualifiedName(*(AD->getParent())); support::fixAnonNS(pname, sfile); std::string mname = support::getQualifiedName(*AD); support::fixAnonNS(mname, sfile); std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str() + "."; writeLog(tolog); BugType *BT = new BugType(Checker, "const_cast used in const function ", "Data Class Const Correctness"); std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(tolog), CELoc); BR.emitReport(std::move(R)); return; } void WalkAST::VisitDeclRefExpr(clang::DeclRefExpr *DRE) { if (clang::VarDecl *D = llvm::dyn_cast_or_null<clang::VarDecl>(DRE->getDecl())) { clang::SourceLocation SL = DRE->getBeginLoc(); if (BR.getSourceManager().isInSystemHeader(SL) || BR.getSourceManager().isInExternCSystemHeader(SL)) return; if (support::isSafeClassName(D->getCanonicalDecl()->getQualifiedNameAsString())) return; ReportDeclRef(DRE); } } void WalkAST::ReportDeclRef(const clang::DeclRefExpr *DRE) { if (const clang::VarDecl *D = llvm::dyn_cast_or_null<clang::VarDecl>(DRE->getDecl())) { clang::QualType t = D->getType(); const clang::Stmt *PS = ParentStmt(DRE); clang::LangOptions LangOpts; LangOpts.CPlusPlus = true; clang::PrintingPolicy Policy(LangOpts); clang::ento::PathDiagnosticLocation CELoc = clang::ento::PathDiagnosticLocation::createBegin(DRE, BR.getSourceManager(), AC); if (support::isSafeClassName(t.getCanonicalType().getAsString())) return; if (D->hasAttr<CMSThreadGuardAttr>() || D->hasAttr<CMSThreadSafeAttr>() || D->hasAttr<CMSSaAllowAttr>()) return; if (D->isStaticLocal() && D->getTSCSpec() != clang::ThreadStorageClassSpecifier::TSCS_thread_local && !support::isConst(t)) { std::string buf; llvm::raw_string_ostream os(buf); os << "Non-const variable '" << support::getQualifiedName(*D) << "' is static local and accessed in statement '"; PS->printPretty(os, nullptr, Policy); os << "'.\n"; std::string pname = support::getQualifiedName(*(AD->getParent())); support::fixAnonNS(pname, sfile); std::string mname = support::getQualifiedName(*AD); support::fixAnonNS(mname, sfile); std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str(); writeLog(tolog); BugType *BT = new BugType( Checker, "ClassChecker : non-const static local variable accessed", "Data Class Const Correctness"); std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc); BR.emitReport(std::move(R)); return; } if (D->isStaticDataMember() && D->getTSCSpec() != clang::ThreadStorageClassSpecifier::TSCS_thread_local && !support::isConst(t)) { std::string buf; llvm::raw_string_ostream os(buf); os << "Non-const variable '" << support::getQualifiedName(*D) << "' is static member data and accessed in statement '"; PS->printPretty(os, nullptr, Policy); os << "'.\n"; std::string pname = support::getQualifiedName(*(AD->getParent())); support::fixAnonNS(pname, sfile); std::string mname = support::getQualifiedName(*AD); support::fixAnonNS(mname, sfile); std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str(); writeLog(tolog); BugType *BT = new BugType(Checker, "Non-const static member variable accessed", "Data Class Const Correctness"); std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc); BR.emitReport(std::move(R)); return; } if ((D->getStorageClass() == clang::SC_Static) && !D->isStaticDataMember() && !D->isStaticLocal() && !support::isConst(t)) { std::string buf; llvm::raw_string_ostream os(buf); os << "Non-const variable '" << support::getQualifiedName(*D) << "' is global static and accessed in statement '"; PS->printPretty(os, nullptr, Policy); os << "'.\n"; std::string pname = support::getQualifiedName(*(AD->getParent())); support::fixAnonNS(pname, sfile); std::string mname = support::getQualifiedName(*AD); support::fixAnonNS(mname, sfile); std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str(); writeLog(tolog); BugType *BT = new BugType(Checker, "Non-const global static variable accessed", "Data Class Const Correctness"); std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc); BR.emitReport(std::move(R)); return; } } } void WalkAST::VisitMemberExpr(clang::MemberExpr *ME) { clang::SourceLocation SL = ME->getExprLoc(); if (BR.getSourceManager().isInSystemHeader(SL) || BR.getSourceManager().isInExternCSystemHeader(SL)) return; const ValueDecl *D = ME->getMemberDecl(); if (D->hasAttr<CMSThreadGuardAttr>() || D->hasAttr<CMSThreadSafeAttr>() || D->hasAttr<CMSSaAllowAttr>()) return; if (!(ME->isImplicitAccess())) return; Stmt *P = AC->getParentMap().getParent(ME); while (AC->getParentMap().hasParent(P)) { if (const clang::UnaryOperator *UO = llvm::dyn_cast_or_null<clang::UnaryOperator>(P)) { WalkAST::CheckUnaryOperator(UO, ME); } if (const clang::BinaryOperator *BO = llvm::dyn_cast_or_null<clang::BinaryOperator>(P)) { WalkAST::CheckBinaryOperator(BO, ME); } if (const clang::CXXOperatorCallExpr *OCE = llvm::dyn_cast_or_null<clang::CXXOperatorCallExpr>(P)) { WalkAST::CheckCXXOperatorCallExpr(OCE, ME); } if (const clang::ExplicitCastExpr *CE = llvm::dyn_cast_or_null<clang::ExplicitCastExpr>(P)) { WalkAST::CheckExplicitCastExpr(CE, ME); } if (const clang::ReturnStmt *RS = llvm::dyn_cast_or_null<clang::ReturnStmt>(P)) { WalkAST::CheckReturnStmt(RS, ME); } if (const clang::CXXConstCastExpr *CCE = llvm::dyn_cast_or_null<clang::CXXConstCastExpr>(P)) { WalkAST::ReportCast(CCE); } const clang::CXXNewExpr *NE = llvm::dyn_cast_or_null<clang::CXXNewExpr>(P); if (NE) break; P = AC->getParentMap().getParent(P); } } void WalkAST::VisitCXXMemberCallExpr(clang::CXXMemberCallExpr *CE) { if (BR.getSourceManager().isInSystemHeader(CE->getExprLoc()) || BR.getSourceManager().isInExternCSystemHeader(CE->getExprLoc())) return; clang::CXXMethodDecl *MD = CE->getMethodDecl(); if (!MD) return; Enqueue(CE); Execute(); Visit(CE->getImplicitObjectArgument()->IgnoreParenCasts()); const Expr *IOA = CE->getImplicitObjectArgument()->IgnoreParenCasts(); const MemberExpr *ME = dyn_cast_or_null<MemberExpr>(IOA); if (!MD->isConst() && ME && ME->isImplicitAccess()) ReportCall(CE); for (int i = 0, j = CE->getNumArgs(); i < j; i++) { if (CE->getArg(i)) { if (const clang::Expr *E = llvm::dyn_cast_or_null<clang::Expr>(CE->getArg(i))) { const clang::MemberExpr *AME = llvm::dyn_cast_or_null<clang::MemberExpr>(E); if (AME && AME->isImplicitAccess()) { clang::ParmVarDecl *PVD = llvm::dyn_cast_or_null<clang::ParmVarDecl>(MD->getParamDecl(i)); assert(PVD); clang::QualType QT = PVD->getOriginalType(); const clang::Type *T = QT.getTypePtr(); if (!support::isConst(QT) && T->isReferenceType() && ME && ME->isImplicitAccess()) ReportCallArg(CE, i); } } } } } void WalkAST::ReportMember(const clang::MemberExpr *ME) { const ValueDecl *D = ME->getMemberDecl(); if (D->hasAttr<CMSThreadGuardAttr>() || D->hasAttr<CMSThreadSafeAttr>() || D->hasAttr<CMSSaAllowAttr>()) return; if (visitingCallExpr) { clang::Expr *IOA = visitingCallExpr->getImplicitObjectArgument(); if (!(IOA->isImplicitCXXThis() || llvm::dyn_cast_or_null<CXXThisExpr>(IOA->IgnoreParenCasts()))) return; } std::string buf; llvm::raw_string_ostream os(buf); clang::ento::PathDiagnosticLocation CELoc; clang::SourceRange R; clang::LangOptions LangOpts; LangOpts.CPlusPlus = true; clang::PrintingPolicy Policy(LangOpts); CELoc = clang::ento::PathDiagnosticLocation::createBegin(ME, BR.getSourceManager(), AC); R = ME->getSourceRange(); os << "Member data '"; ME->printPretty(os, nullptr, Policy); os << "' is directly or indirectly modified in const function\n"; std::string pname = support::getQualifiedName(*(AD->getParent())); support::fixAnonNS(pname, sfile); std::string mname = support::getQualifiedName(*AD); support::fixAnonNS(mname, sfile); std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str(); writeLog(tolog); BR.EmitBasicReport( AD, Checker, "Member data modified in const function", "Data Class Const Correctness", os.str(), CELoc); } void WalkAST::ReportCall(const clang::CXXMemberCallExpr *CE) { const clang::CXXRecordDecl *RD = CE->getRecordDecl(); const clang::CXXMethodDecl *MD = CE->getMethodDecl(); if (!RD || support::isSafeClassName(RD->getQualifiedNameAsString())) return; std::string buf; llvm::raw_string_ostream os(buf); clang::ento::PathDiagnosticLocation CELoc = clang::ento::PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); clang::LangOptions LangOpts; LangOpts.CPlusPlus = true; clang::PrintingPolicy Policy(LangOpts); os << "call expr '"; CE->printPretty(os, nullptr, Policy); os << "' with implicit object argument '"; CE->getImplicitObjectArgument()->IgnoreParenCasts()->printPretty(os, nullptr, Policy); os << "'"; os << "' is a non-const member function '" << support::getQualifiedName(*MD); os << "' that could modify member data object of type '" << support::getQualifiedName(*RD) << "'\n"; std::string pname = support::getQualifiedName(*(AD->getParent())); support::fixAnonNS(pname, sfile); std::string mname = support::getQualifiedName(*AD); support::fixAnonNS(mname, sfile); std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str(); if (support::isSafeClassName(support::getQualifiedName(*MD))) return; writeLog(tolog); BugType *BT = new BugType( Checker, "Non-const member function could modify member data object", "Data Class Const Correctness"); std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc); BR.emitReport(std::move(R)); } void WalkAST::ReportCast(const clang::ExplicitCastExpr *CE) { std::string buf; llvm::raw_string_ostream os(buf); clang::LangOptions LangOpts; LangOpts.CPlusPlus = true; clang::PrintingPolicy Policy(LangOpts); os << "Const qualifier of member data object"; os << " was removed via cast expression '"; std::string pname = support::getQualifiedName(*(AD->getParent())); support::fixAnonNS(pname, sfile); std::string mname = support::getQualifiedName(*AD); support::fixAnonNS(mname, sfile); std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str(); clang::ento::PathDiagnosticLocation CELoc = clang::ento::PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); writeLog(tolog); BugType *BT = new BugType(Checker, "Const cast away from member data in const function", "Data Class Const Correctness"); std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc); BR.emitReport(std::move(R)); } void WalkAST::ReportCallArg(const clang::CXXMemberCallExpr *CE, const int i) { std::string buf; llvm::raw_string_ostream os(buf); clang::LangOptions LangOpts; LangOpts.CPlusPlus = true; clang::PrintingPolicy Policy(LangOpts); assert(llvm::dyn_cast<clang::CXXMemberCallExpr>(CE)); clang::CXXMethodDecl *CMD = llvm::dyn_cast<clang::CXXMemberCallExpr>(CE)->getMethodDecl(); const clang::MemberExpr *E = llvm::dyn_cast<clang::MemberExpr>(CE->getArg(i)); assert(E); clang::ValueDecl *VD = llvm::dyn_cast<clang::ValueDecl>(E->getMemberDecl()); assert(VD); os << "Member data '" << VD->getQualifiedNameAsString(); os << "' is passed to a non-const reference parameter"; os << " of CXX method '" << CMD->getQualifiedNameAsString() << "' in const function"; os << "\n"; std::string pname = support::getQualifiedName(*(AD->getParent())); support::fixAnonNS(pname, sfile); std::string mname = support::getQualifiedName(*AD); support::fixAnonNS(mname, sfile); std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str(); clang::ento::PathDiagnosticLocation ELoc = clang::ento::PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); writeLog(tolog); BR.EmitBasicReport(CE->getCalleeDecl(), Checker, "Member data passed to non-const reference", "Data Class Const Correctness", os.str(), ELoc); } void WalkAST::ReportCallReturn(const clang::ReturnStmt *RS) { std::string buf; llvm::raw_string_ostream os(buf); clang::LangOptions LangOpts; LangOpts.CPlusPlus = true; clang::PrintingPolicy Policy(LangOpts); os << "Returns a pointer or reference to a non-const member data object "; os << " or a const std::vector<*> or const std::vector<*>& "; os << "in const function in statement '"; RS->printPretty(os, nullptr, Policy); os << "\n"; const clang::CXXMethodDecl *MD = llvm::cast<clang::CXXMethodDecl>(AD); clang::ento::PathDiagnosticLocation CELoc = clang::ento::PathDiagnosticLocation::createBegin(RS, BR.getSourceManager(), AC); std::string pname = support::getQualifiedName(*(AD->getParent())); support::fixAnonNS(pname, sfile); std::string mname = support::getQualifiedName(*AD); support::fixAnonNS(mname, sfile); std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str(); writeLog(tolog); clang::ASTContext &Ctx = AC->getASTContext(); clang::QualType RQT = MD->getCallResultType(); clang::QualType RTy = Ctx.getCanonicalType(RQT); if ((RTy->isPointerType() || RTy->isReferenceType())) { if (!support::isConst(RTy)) { BugType *BT = new BugType(Checker, "Const function returns pointer or reference to non-const member data object", "Data Class Const Correctness"); std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc); BR.emitReport(std::move(R)); } } std::string svname = "const class std::vector<"; std::string rtname = RTy.getAsString(); if ((RTy->isReferenceType() || RTy->isRecordType()) && support::isConst(RTy) && rtname.substr(0, svname.length()) == svname) { BugType *BT = new BugType(Checker, "Const function returns member data object of type const std::vector<*> or const std::vector<*>&", "Data Class Const Correctness"); std::unique_ptr<BasicBugReport> R = std::make_unique<BasicBugReport>(*BT, llvm::StringRef(os.str()), CELoc); BR.emitReport(std::move(R)); } } void ClassChecker::checkASTDecl(const clang::CXXRecordDecl *RD, clang::ento::AnalysisManager &mgr, clang::ento::BugReporter &BR) const { const clang::SourceManager &SM = BR.getSourceManager(); const char *sfile = SM.getPresumedLoc(RD->getLocation()).getFilename(); if (!support::isCmsLocalFile(sfile)) return; std::string buf; llvm::raw_string_ostream os(buf); std::string name = RD->getQualifiedNameAsString(); if (!support::isDataClass(name)) return; for (auto I = RD->field_begin(), E = RD->field_end(); I != E; ++I) { const FieldDecl *D = (*I); if (D->hasAttr<CMSThreadGuardAttr>() || D->hasAttr<CMSThreadSafeAttr>() || D->hasAttr<CMSSaAllowAttr>()) return; if (D->isMutable()) { clang::QualType t = D->getType(); clang::ento::PathDiagnosticLocation DLoc = clang::ento::PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); if (support::isSafeClassName(t.getCanonicalType().getAsString())) return; if (!support::isDataClass(support::getQualifiedName(*RD))) return; std::string buf; llvm::raw_string_ostream os(buf); os << "Mutable member '" << t.getCanonicalType().getAsString() << " " << *D << "' in data class '" << support::getQualifiedName(*RD) << "', might be thread-unsafe when accessing via a const handle."; BR.EmitBasicReport(D, this, "Mutable member in data class", "Data Class Const Correctness", os.str(), DLoc); std::string pname = support::getQualifiedName(*(RD)); support::fixAnonNS(pname, sfile); std::string mname = support::getQualifiedName(*D); support::fixAnonNS(mname, sfile); std::string tolog = "data class '" + pname + "' mutable member '" + mname + "' Warning: " + os.str(); writeLog(tolog); } } // Check the class methods (member methods). for (clang::CXXRecordDecl::method_iterator I = RD->method_begin(), E = RD->method_end(); I != E; ++I) { if (!llvm::isa<clang::CXXMethodDecl>((*I))) continue; if (!(*I)->isConst()) continue; clang::CXXMethodDecl *MD = llvm::cast<clang::CXXMethodDecl>((*I)->getMostRecentDecl()); if (MD->hasAttr<CMSThreadGuardAttr>() || MD->hasAttr<CMSThreadSafeAttr>() || MD->hasAttr<CMSSaAllowAttr>()) continue; if (MD->hasBody()) { clang::Stmt *Body = MD->getBody(); WalkAST walker(this, BR, mgr.getAnalysisDeclContext(MD), MD, sfile); walker.Visit(Body); clang::QualType RQT = MD->getCallResultType(); clang::ASTContext &Ctx = BR.getContext(); clang::QualType RTy = Ctx.getCanonicalType(RQT); clang::ento::PathDiagnosticLocation ELoc = clang::ento::PathDiagnosticLocation::createBegin(MD, SM); if ((RTy->isPointerType() || RTy->isReferenceType()) && (!support::isConst(RTy)) && (support::getQualifiedName(*MD).find("clone") == std::string::npos)) { std::string buf; llvm::raw_string_ostream os(buf); os << MD->getQualifiedNameAsString() << " is a const member function that returns a pointer or reference to a non-const object \n"; std::string pname = support::getQualifiedName(*(MD->getParent())); support::fixAnonNS(pname, sfile); std::string mname = support::getQualifiedName(*MD); support::fixAnonNS(mname, sfile); std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str(); writeLog(tolog); BR.EmitBasicReport(MD, this, "Const function returns pointer or reference to non-const object.", "Data Class Const Correctness", os.str(), ELoc); } std::string svname = "const class std::vector<"; std::string rtname = RTy.getAsString(); if ((RTy->isReferenceType() || RTy->isRecordType()) && support::isConst(RTy) && rtname.substr(0, svname.length()) == svname) { const CXXRecordDecl *RD; if (RTy->isRecordType()) RD = RTy->getAsCXXRecordDecl(); else RD = RTy->getPointeeCXXRecordDecl(); const ClassTemplateSpecializationDecl *SD = dyn_cast<ClassTemplateSpecializationDecl>(RD); for (unsigned J = 0, F = SD->getTemplateArgs().size(); J != F; ++J) { if (SD->getTemplateArgs().get(J).getKind() == clang::TemplateArgument::Type) { const QualType QAT = SD->getTemplateArgs().get(J).getAsType(); if (QAT->isPointerType() && !support::isConst(QAT)) { std::string buf; llvm::raw_string_ostream os(buf); std::string pname = support::getQualifiedName(*(MD->getParent())); support::fixAnonNS(pname, sfile); std::string mname = support::getQualifiedName(*MD); support::fixAnonNS(mname, sfile); os << mname << " is a const member function that returns an object of type const std::vector<*> or const " "std::vector<*>& " << rtname << "\n"; std::string tolog = "data class '" + pname + "' const function '" + mname + "' Warning: " + os.str(); writeLog(tolog); BR.EmitBasicReport(MD, this, "Const function returns const std::vector<*> or const std::vector<*>&", "Data Class Const Correctness", os.str(), ELoc); } } } } } } /* end of methods loop */ } //end of class } // namespace clangcms