File indexing completed on 2024-10-08 23:10:07
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
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
0060 DFSWorkList WList;
0061
0062
0063
0064
0065
0066 enum Kind {
0067 NotVisited,
0068 Visiting,
0069
0070
0071 Visited
0072
0073 };
0074
0075
0076 llvm::DenseMap<const clang::CXXMemberCallExpr *, Kind> VisitedFunctions;
0077
0078
0079
0080
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
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
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
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
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
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 }
0769
0770 }
0771
0772 }