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