void ImplicitCasts::VisitStmt(clang::Stmt *stmt) { if (isMacroToIgnore(stmt->getLocStart())) return; // Lets check only in function calls. Otherwise there are too many false positives, it's common // to implicit cast to bool when checking pointers for validity, like if (ptr) CallExpr *callExpr = dyn_cast<CallExpr>(stmt); CXXConstructExpr *ctorExpr = dyn_cast<CXXConstructExpr>(stmt); if (!callExpr && !ctorExpr) return; FunctionDecl *func = callExpr ? callExpr->getDirectCallee() : ctorExpr->getConstructor(); if (isInterestingFunction(func)) { // Check pointer->bool implicit casts iterateCallExpr<CallExpr>(callExpr, this); iterateCallExpr<CXXConstructExpr>(ctorExpr, this); } else if (isInterestingFunction2(func)) { // Check bool->int implicit casts iterateCallExpr2<CallExpr>(callExpr, this, m_parentMap); iterateCallExpr2<CXXConstructExpr>(ctorExpr, this, m_parentMap); } }
// Returns the first occurrence of a QLatin1String(char*) CTOR call Latin1Expr QStringAllocations::qlatin1CtorExpr(Stmt *stm, ConditionalOperator * &ternary) { if (!stm) return {}; CXXConstructExpr *constructExpr = dyn_cast<CXXConstructExpr>(stm); if (constructExpr) { CXXConstructorDecl *ctor = constructExpr->getConstructor(); const int numArgs = ctor->getNumParams(); if (StringUtils::isOfClass(ctor, "QLatin1String")) { if (Utils::containsStringLiteral(constructExpr, /*allowEmpty=*/ false, 2)) return {constructExpr, /*enableFixits=*/ numArgs == 1}; if (Utils::userDefinedLiteral(constructExpr, "QLatin1String", lo())) return {constructExpr, /*enableFixits=*/ false}; } } if (!ternary) ternary = dyn_cast<ConditionalOperator>(stm); for (auto child : stm->children()) { auto expr = qlatin1CtorExpr(child, ternary); if (expr.isValid()) return expr; } return {}; }
// Returns the first occurrence of a QLatin1String(char*) CTOR call static CXXConstructExpr *qlatin1CtorExpr(Stmt *stm, ConditionalOperator * &ternary) { if (!stm) return nullptr; CXXConstructExpr *constructExpr = dyn_cast<CXXConstructExpr>(stm); if (constructExpr) { CXXConstructorDecl *ctor = constructExpr->getConstructor(); if (StringUtils::isOfClass(ctor, "QLatin1String") && hasCharPtrArgument(ctor, 1)) { if (Utils::containsStringLiteral(constructExpr, /*allowEmpty=*/ false, 2)) return constructExpr; } } if (!ternary) ternary = dyn_cast<ConditionalOperator>(stm); for (auto child : stm->children()) { auto expr = qlatin1CtorExpr(child, ternary); if (expr) return expr; } return nullptr; }
bool Utils::insideCTORCall(ParentMap *map, Stmt *s, const std::vector<string> &anyOf) { if (!s) return false; CXXConstructExpr *expr = dyn_cast<CXXConstructExpr>(s); if (expr && expr->getConstructor() && clazy_std::contains(anyOf, expr->getConstructor()->getNameAsString())) { return true; } return insideCTORCall(map, HierarchyUtils::parent(map, s), anyOf); }
void Qt4_QStringFromArray::VisitStmt(clang::Stmt *stm) { CXXConstructExpr *ctorExpr = dyn_cast<CXXConstructExpr>(stm); CXXOperatorCallExpr *operatorCall = dyn_cast<CXXOperatorCallExpr>(stm); CXXMemberCallExpr *memberCall = dyn_cast<CXXMemberCallExpr>(stm); if (!ctorExpr && !operatorCall && !memberCall) return; vector<FixItHint> fixits; bool is_char_array = false; bool is_byte_array = false; string methodName; string message; if (ctorExpr) { CXXConstructorDecl *ctorDecl = ctorExpr->getConstructor(); if (!isInterestingCtorCall(ctorDecl, is_char_array, is_byte_array)) return; fixits = fixCtorCall(ctorExpr); if (is_char_array) { message = "QString(const char *) ctor being called"; } else { message = "QString(QByteArray) ctor being called"; } } else if (operatorCall) { if (!isInterestingOperatorCall(operatorCall, /*by-ref*/methodName, is_char_array, is_byte_array)) return; fixits = fixOperatorCall(operatorCall); } else if (memberCall) { if (!isInterestingMethodCall(memberCall->getMethodDecl(), /*by-ref*/methodName, is_char_array, is_byte_array)) return; fixits = fixMethodCallCall(memberCall); } else { return; } if (operatorCall || memberCall) { if (is_char_array) { message = "QString::" + methodName + "(const char *) being called"; } else { message = "QString::" + methodName + "(QByteArray) being called"; } } emitWarning(stm->getLocStart(), message, fixits); }
void QStringAllocations::VisitCtor(Stmt *stm) { CXXConstructExpr *ctorExpr = dyn_cast<CXXConstructExpr>(stm); if (!Utils::containsStringLiteral(ctorExpr, /**allowEmpty=*/ true)) return; CXXConstructorDecl *ctorDecl = ctorExpr->getConstructor(); if (!StringUtils::isOfClass(ctorDecl, "QString")) return; static const vector<string> blacklistedParentCtors = { "QRegExp", "QIcon" }; if (Utils::insideCTORCall(m_parentMap, stm, blacklistedParentCtors)) { // https://blogs.kde.org/2015/11/05/qregexp-qstringliteral-crash-exit return; } if (!isOptionSet("no-msvc-compat")) { InitListExpr *initializerList = HierarchyUtils::getFirstParentOfType<InitListExpr>(m_parentMap, ctorExpr); if (initializerList != nullptr) return; // Nothing to do here, MSVC doesn't like it StringLiteral *lt = stringLiteralForCall(stm); if (lt && lt->getNumConcatenated() > 1) { return; // Nothing to do here, MSVC doesn't like it } } bool isQLatin1String = false; string paramType; if (hasCharPtrArgument(ctorDecl, 1)) { paramType = "const char*"; } else if (ctorDecl->param_size() == 1 && StringUtils::hasArgumentOfType(ctorDecl, "QLatin1String", lo())) { paramType = "QLatin1String"; isQLatin1String = true; } else { return; } string msg = string("QString(") + paramType + string(") being called"); if (isQLatin1String) { ConditionalOperator *ternary = nullptr; Latin1Expr qlatin1expr = qlatin1CtorExpr(stm, ternary); if (!qlatin1expr.isValid()) { return; } auto qlatin1Ctor = qlatin1expr.qlatin1ctorexpr; vector<FixItHint> fixits; if (qlatin1expr.enableFixit && isFixitEnabled(QLatin1StringAllocations)) { if (!qlatin1Ctor->getLocStart().isMacroID()) { if (!ternary) { fixits = fixItReplaceWordWithWord(qlatin1Ctor, "QStringLiteral", "QLatin1String", QLatin1StringAllocations); bool shouldRemoveQString = qlatin1Ctor->getLocStart().getRawEncoding() != stm->getLocStart().getRawEncoding() && dyn_cast_or_null<CXXBindTemporaryExpr>(HierarchyUtils::parent(m_parentMap, ctorExpr)); if (shouldRemoveQString) { // This is the case of QString(QLatin1String("foo")), which we just fixed to be QString(QStringLiteral("foo)), so now remove QString auto removalFixits = FixItUtils::fixItRemoveToken(ci(), ctorExpr, true); if (removalFixits.empty()) { queueManualFixitWarning(ctorExpr->getLocStart(), QLatin1StringAllocations, "Internal error: invalid start or end location"); } else { clazy_std::append(removalFixits, fixits); } } } else { fixits = fixItReplaceWordWithWordInTernary(ternary); } } else { queueManualFixitWarning(qlatin1Ctor->getLocStart(), QLatin1StringAllocations, "Can't use QStringLiteral in macro"); } } emitWarning(stm->getLocStart(), msg, fixits); } else { vector<FixItHint> fixits; if (clazy_std::hasChildren(ctorExpr)) { auto pointerDecay = dyn_cast<ImplicitCastExpr>(*(ctorExpr->child_begin())); if (clazy_std::hasChildren(pointerDecay)) { StringLiteral *lt = dyn_cast<StringLiteral>(*pointerDecay->child_begin()); if (lt && isFixitEnabled(CharPtrAllocations)) { Stmt *grandParent = HierarchyUtils::parent(m_parentMap, lt, 2); Stmt *grandGrandParent = HierarchyUtils::parent(m_parentMap, lt, 3); Stmt *grandGrandGrandParent = HierarchyUtils::parent(m_parentMap, lt, 4); if (grandParent == ctorExpr && grandGrandParent && isa<CXXBindTemporaryExpr>(grandGrandParent) && grandGrandGrandParent && isa<CXXFunctionalCastExpr>(grandGrandGrandParent)) { // This is the case of QString("foo"), replace QString const bool literalIsEmpty = lt->getLength() == 0; if (literalIsEmpty && HierarchyUtils::getFirstParentOfType<MemberExpr>(m_parentMap, ctorExpr) == nullptr) fixits = fixItReplaceWordWithWord(ctorExpr, "QLatin1String", "QString", CharPtrAllocations); else if (!ctorExpr->getLocStart().isMacroID()) fixits = fixItReplaceWordWithWord(ctorExpr, "QStringLiteral", "QString", CharPtrAllocations); else queueManualFixitWarning(ctorExpr->getLocStart(), CharPtrAllocations, "Can't use QStringLiteral in macro."); } else { auto parentMemberCallExpr = HierarchyUtils::getFirstParentOfType<CXXMemberCallExpr>(m_parentMap, lt, /*maxDepth=*/6); // 6 seems like a nice max from the ASTs I've seen string replacement = "QStringLiteral"; if (parentMemberCallExpr) { FunctionDecl *fDecl = parentMemberCallExpr->getDirectCallee(); if (fDecl) { CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(fDecl); if (method && betterTakeQLatin1String(method, lt)) { replacement = "QLatin1String"; } } } fixits = fixItRawLiteral(lt, replacement); } } } } emitWarning(stm->getLocStart(), msg, fixits); } }
/* this helper function is called when the traversal reaches a node of type Stmt */ void StmtHelper(Stmt *x){ //variable used for <cond> </cond> //bool condition = false; bool isElse = false; if(x != NULL){ string output = ""; //find current level and next level int intLevel = getLevelStmt(x); int intNextLevel = intLevel+1; //convert them both to strings to use for output string level; string nextLevel; stringstream ss; ss << intLevel; level = ss.str(); stringstream ss2; ss2 << intNextLevel; nextLevel = ss2.str(); const Stmt* parent = getStmtParent(x, Context); //PROBLEM if(x->getStmtClassName() != std::string("ForStmt") && isFlowControl(x, Context)){ //return; } //if the parent is calling any type of funciton then this node should be enclosed in <args> </args> string filename; if(callStackDebug && !callStack.empty()){ cerr << "stmt: call stack top: " << callStack.top()->getStmtClassName() << endl; } while(!callStack.empty() && numClosingArgsNeeded > 0 && !isParentStmt(parent, callStack.top()->getStmtClassName())){ if(debugPrint){ cerr << "adding args" << endl; } numClosingArgsNeeded--; output += "</args,1>\n"; callStack.pop(); if(callStackDebug){ cerr << "popping" << endl; printCallStack(); } } if(isParentStmtInCurFile(x,"CXXConstructExpr") && isParentStmt(x, "CXXConstructExpr")){ if(debugPrint){ cerr << "setting previousConstructorCall to true" << endl; } }else if(isParentStmtInCurFile(x,"CXXTemporaryObjectExpr") && isParentStmt(x, "CXXTemporaryObjectExpr")){ if(debugPrint){ cerr << "setting previousTempConstructorCallArg" << endl; } }else if(isParentStmt(x, "CallExpr")){ if(debugPrint){ cerr << "setting previousCallArgs to true" << endl; } }else if(isParentStmt(x, "CXXMemberCallExpr")){ if(debugPrint){ cerr << "setting previousMemberCallArgs to true" << endl; } } //if the parent is a variable declaration then this node should be encolsed in <decl> </decl> if(isParentStmt(x, "Var")){ previousRhsDecl = true; if(debugPrint){ cout << "setting prev var to true" << endl; } }else if(previousRhsDecl && numClosingVarsNeeded > 0){ //if the current node is not a child of a variable declaration //but the previous node was a child of a variable declation //then we know to print a </decl> output +="</variableDecl,1>\n"; numClosingVarsNeeded--; previousRhsDecl = false; } if(parent != NULL && strcmp(parent->getStmtClassName(), "IfStmt") == 0){ if(debugPrint){ cerr << "possibly an if statement" << endl; } //find the first child of the if statemt const Stmt* firstChild = NULL; auto children = parent->children(); for(const Stmt* child : children){ if(child != NULL){ firstChild = child; break; } } //if the first child is the current node, then we know it is part of the condition if(firstChild != NULL && x->getLocStart() == firstChild->getLocStart()){ if(debugPrint){ cerr << "part of the condition" << endl; } prevCondition = true; }else if(prevCondition){ output +="</cond,1>\n"; prevCondition = false; } //find if else const IfStmt* ifstmt = (IfStmt*) parent; const Stmt* elseStmt = ifstmt->getElse(); if(elseStmt != NULL){ if(debugPrint){ cout << "checking if " << x->getLocStart().printToString(Context->getSourceManager()); cout << " == " << elseStmt->getLocStart().printToString(Context->getSourceManager()); cout << " : " << (x->getLocStart() == elseStmt->getLocStart()) << endl; } if(x->getLocStart() == elseStmt->getLocStart()){ isElse = true; } } } string node = x->getStmtClassName(); if(node == "ReturnStmt"){ output += "<return"; }else if(node == "ForStmt"){ output += "<forLoop"; }else if(node == "WhileStmt"){ output += "<whileLoop"; }else if(node == "DoStmt"){ output += "<do"; }else if(node == "IfStmt"){ if(parent->getStmtClassName() != std::string("IfStmt")){ stringstream ssminus; ssminus << (intLevel-1); output += "<ifBlock," + ssminus.str() + ">\n"; intLevel += 1; stringstream ssif; ssif << intLevel; level = ssif.str(); } output += "<ifStatement"; }else if(node == "SwitchStmt"){ output += "<switch"; }else if(node == "CaseStmt"){ output += "<case"; }else if(node == "CXXMemberCallExpr"){ CXXMemberCallExpr* ce = (CXXMemberCallExpr*) x; Expr* obj = ce->getImplicitObjectArgument(); CallExpr* expr = (CallExpr*) x; output += "<object: "; QualType qt = obj->getType(); output += qt.getBaseTypeIdentifier()->getName().str(); output += "; calling func: "; output += expr->getDirectCallee()->getNameInfo().getAsString(); output += ", " + level + ">\n"; output += "<args"; numClosingArgsNeeded++; callStack.push(x); if(callStackDebug){ cerr << "pushing" << endl; printCallStack(); } }else if(node == "CallExpr"){ CallExpr* expr = (CallExpr*) x; output += "<calling func: "; output += expr->getDirectCallee()->getNameInfo().getAsString(); output += ", " + level + ">\n"; output += "<args"; numClosingArgsNeeded++; callStack.push(x); if(callStackDebug){ cerr << "pushing" << endl; printCallStack(); } }else if(node == "CXXConstructExpr"){ CXXConstructExpr* ce = (CXXConstructExpr*) x; //Decl* CD = ce->getConstructor(); string filename; //if(isInCurFile(Context, CD, filename)){ CXXMethodDecl* MD = ce->getConstructor(); output += "<calling func: "; output += MD->getNameInfo().getAsString(); output += "," + level + ">\n"; output += "<args"; numClosingArgsNeeded++; callStack.push(x); if(callStackDebug){ cerr << "pushing" << endl; printCallStack(); } //} }else if(node == "BinaryOperator"){ BinaryOperator* binaryOp = (BinaryOperator*) x; if(binaryOp->isAssignmentOp()){ output += "<assignment"; }else if(binaryOp->isComparisonOp()){ output += "<comparison"; }else{ output += "<binaryOp"; } }else if(node == "UnaryOperator"){ UnaryOperator* uo = (UnaryOperator*) x; string op = uo->getOpcodeStr(uo->getOpcode()).str(); if(op != "-"){ output += "<unaryOp"; } }else if(node == "CompoundAssignOperator"){ output += "<augAssign"; }else if(node == "CompoundStmt"){ if(isElse){ output += "<elseStatement"; }else{ output += "<compoundStmt"; } }else if(node == "CXXThrowExpr"){ output += "<raisingException"; }else if(node == "CXXTryStmt"){ output += "<try"; }else if(node == "CXXCatchStmt"){ output += "<except"; }else if(node == "CXXOperatorCallExpr"){ CXXOperatorCallExpr* ce = (CXXOperatorCallExpr*) x; if(ce->isAssignmentOp()){ output += "<assignment"; } }else if(node == "CXXTemporaryObjectExpr"){ CXXTemporaryObjectExpr* ce = (CXXTemporaryObjectExpr*) x; Decl* CD = ce->getConstructor(); string filename; if(isInCurFile(Context, CD, filename)){ CXXMethodDecl* MD = ce->getConstructor(); output += "<calling func: "; output += MD->getNameInfo().getAsString(); output += "," + level + ">\n"; output += "<args"; numClosingArgsNeeded++; callStack.push(x); if(callStackDebug){ cerr << "pushing" << endl; printCallStack(); } } }else if(node == "DeclRefExpr"){ if(parent != NULL && parent->getStmtClassName() == std::string("ImplicitCastExpr")){ DeclRefExpr* dr = (DeclRefExpr*) x; ValueDecl* d = (ValueDecl*) dr->getDecl(); //cout << d->getQualType().getAsString() << endl; if(d != NULL){ QualType qt = d->getType(); //cout << qt.getAsString() << endl; if(qt.getAsString() == "std::vector<int, class std::allocator<int> >::const_reference (std::vector::size_type) const noexcept"){ //string type = io->getName().str(); //cout << type << endl; //if(type == "vector"){ output += "<expr"; //} } } } }else{ if(allNodes){ output += "<"; output += node; output += ">"; } } if(output.size() != 0 && !endsIn(output, "</cond,1>\n") && !endsIn(output,"</variableDecl,1>\n") && !endsIn(output,"</args,1>\n") && !endsIn(output,">") && !endsIn(output, ">\n")){ output += ", " + level + ">"; cout << output << endl; output = ""; }else if(output.size() != 0){ cout << output << endl; output = ""; if(debugPrint){ cerr << "printing output" << endl; } } } }