File indexing completed on 2025-01-22 07:34:33
0001 #include "UnnecessaryMutableChecker.h"
0002 #include "clang/Basic/SourceManager.h"
0003 #include "llvm/Support/Path.h"
0004 #include "CmsSupport.h"
0005
0006 namespace clangcms {
0007
0008
0009 void UnnecessaryMutableChecker::checkASTDecl(const clang::CXXRecordDecl *RD,
0010 clang::ento::AnalysisManager &Mgr,
0011 clang::ento::BugReporter &BR) const {
0012 if (!RD->hasDefinition())
0013 return;
0014
0015
0016 const clang::SourceManager &SM = Mgr.getASTContext().getSourceManager();
0017 auto MainFileID = SM.getMainFileID();
0018 if (MainFileID.isInvalid()) {
0019 llvm::errs() << "Main file is invalid, skipping\n";
0020 return;
0021 }
0022 std::string MainFileStr = SM.getFileEntryRefForID(MainFileID)->getName().str();
0023
0024 std::string BaseNameStr =
0025 llvm::sys::path::parent_path(MainFileStr).str() + "/" + llvm::sys::path::stem(MainFileStr).str();
0026 clang::StringRef CurrentFile = SM.getFilename(RD->getLocation());
0027 if (!CurrentFile.ends_with(BaseNameStr + ".cpp") && !CurrentFile.ends_with(BaseNameStr + ".h")) {
0028 return;
0029 }
0030
0031
0032 std::string ClassName = RD->getNameAsString();
0033 if (support::isSafeClassName(ClassName)) {
0034 return;
0035 }
0036
0037
0038 clang::ento::PathDiagnosticLocation PathLoc = clang::ento::PathDiagnosticLocation::createBegin(RD, SM);
0039
0040 if (!m_exception.reportMutableMember(PathLoc, BR)) {
0041 return;
0042 }
0043
0044
0045 for (const auto *Field : RD->fields()) {
0046 if (!Field->isMutable()) {
0047 continue;
0048 }
0049
0050
0051
0052
0053
0054 if (Field->getAccess() != clang::AS_private) {
0055 return;
0056 }
0057
0058
0059 if (support::isStdAtomic(Field)) {
0060 return;
0061 }
0062
0063
0064 const clang::AttrVec &FAttrs = Field->getAttrs();
0065 for (const auto *A : FAttrs) {
0066 if (clang::isa<clang::CMSThreadGuardAttr>(A) || clang::isa<clang::CMSThreadSafeAttr>(A) ||
0067 clang::isa<clang::CMSSaAllowAttr>(A)) {
0068 return;
0069 }
0070 }
0071
0072
0073 if (!isMutableMemberModified(Field, RD)) {
0074 clang::SourceLocation Loc = Field->getLocation();
0075 if (Loc.isValid()) {
0076 clang::ento::PathDiagnosticLocation FieldLoc(Loc, SM);
0077 if (!BT) {
0078 BT = std::make_unique<clang::ento::BugType>(this, "Unnecessarily Mutable Member", "Coding Practices");
0079 }
0080 BR.EmitBasicReport(Field,
0081 this,
0082 "Useless mutable field",
0083 "ConstThreadSafety",
0084 "The mutable field '" + Field->getQualifiedNameAsString() +
0085 "' is not modified in any public const methods",
0086 FieldLoc);
0087 }
0088 }
0089 }
0090 }
0091
0092 bool UnnecessaryMutableChecker::isMutableMemberModified(const clang::FieldDecl *Field,
0093 const clang::CXXRecordDecl *RD) const {
0094 for (const auto *Method : RD->methods()) {
0095 if (!Method->isConst())
0096 continue;
0097
0098 if (const auto *Body = Method->getBody()) {
0099 for (const auto *Stmt : Body->children()) {
0100 if (Stmt) {
0101 if (analyzeStmt(Stmt, Field))
0102 return true;
0103 }
0104 }
0105 }
0106 }
0107
0108 return false;
0109 }
0110
0111 bool UnnecessaryMutableChecker::analyzeStmt(const clang::Stmt *S, const clang::FieldDecl *Field) const {
0112 if (const auto *UnaryOp = clang::dyn_cast<clang::UnaryOperator>(S)) {
0113 if (UnaryOp->isIncrementDecrementOp()) {
0114 if (const auto *ME = clang::dyn_cast<clang::MemberExpr>(UnaryOp->getSubExpr())) {
0115 if (const auto *FD = clang::dyn_cast<clang::FieldDecl>(ME->getMemberDecl())) {
0116 if (FD == Field) {
0117 return true;
0118 }
0119 }
0120 }
0121 }
0122 } else if (const auto *BinaryOp = clang::dyn_cast<clang::BinaryOperator>(S)) {
0123 if (BinaryOp->isAssignmentOp() || BinaryOp->isCompoundAssignmentOp()) {
0124 if (const auto *LHS = clang::dyn_cast<clang::MemberExpr>(BinaryOp->getLHS())) {
0125 if (const auto *FD = clang::dyn_cast<clang::FieldDecl>(LHS->getMemberDecl())) {
0126 if (FD == Field) {
0127 return true;
0128 }
0129 }
0130 }
0131 }
0132 } else if (const auto *Call = clang::dyn_cast<clang::CXXMemberCallExpr>(S)) {
0133 if (const auto *Callee = Call->getMethodDecl()) {
0134 if (!Callee->isConst()) {
0135 if (const auto *ImplicitObj = Call->getImplicitObjectArgument()) {
0136 if (const auto *ME = clang::dyn_cast<clang::MemberExpr>(ImplicitObj)) {
0137 if (const auto *FD = clang::dyn_cast<clang::FieldDecl>(ME->getMemberDecl())) {
0138 if (FD == Field) {
0139 return true;
0140 }
0141 }
0142 }
0143 }
0144 }
0145 }
0146 } else if (const auto *OpCall = clang::dyn_cast<clang::CXXOperatorCallExpr>(S)) {
0147 if (OpCall->isAssignmentOp()) {
0148 if (const auto *ME = llvm::dyn_cast<clang::MemberExpr>(OpCall->getArg(0))) {
0149 if (const auto *FD = clang::dyn_cast<clang::FieldDecl>(ME->getMemberDecl())) {
0150 if (FD == Field) {
0151 return true;
0152 }
0153 }
0154 }
0155 }
0156 }
0157
0158
0159 for (const auto *Child : S->children()) {
0160 if (Child) {
0161 if (analyzeStmt(Child, Field)) {
0162 return true;
0163 }
0164 }
0165 }
0166
0167 return false;
0168 }
0169 }