void QDeleteAll::VisitStmt(clang::Stmt *stmt) { // Find a call to QMap/QSet/QHash::values CXXMemberCallExpr *valuesCall = dyn_cast<CXXMemberCallExpr>(stmt); if (valuesCall && valuesCall->getDirectCallee() && valuesCall->getDirectCallee()->getNameAsString() == "values") { const std::string valuesClassName = valuesCall->getMethodDecl()->getParent()->getNameAsString(); if (valuesClassName == "QMap" || valuesClassName == "QSet" || valuesClassName == "QHash") { // QMultiHash and QMultiMap automatically supported // Once found see if the first parent call is qDeleteAll int i = 1; Stmt *p = Utils::parent(m_parentMap, stmt, i); while (p) { CallExpr *pc = dyn_cast<CallExpr>(p); if (pc) { if (pc->getDirectCallee() && pc->getDirectCallee()->getNameAsString() == "qDeleteAll") { emitWarning(p->getLocStart(), "Calling qDeleteAll with " + valuesClassName + "::values, call qDeleteAll on the container itself"); } break; } ++i; p = Utils::parent(m_parentMap, stmt, i); } } } }
/// Build calls to await_ready, await_suspend, and await_resume for a co_await /// expression. static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, VarDecl *CoroPromise, SourceLocation Loc, Expr *E) { OpaqueValueExpr *Operand = new (S.Context) OpaqueValueExpr(Loc, E->getType(), VK_LValue, E->getObjectKind(), E); // Assume invalid until we see otherwise. ReadySuspendResumeResult Calls = {{}, Operand, /*IsInvalid=*/true}; ExprResult CoroHandleRes = buildCoroutineHandle(S, CoroPromise->getType(), Loc); if (CoroHandleRes.isInvalid()) return Calls; Expr *CoroHandle = CoroHandleRes.get(); const StringRef Funcs[] = {"await_ready", "await_suspend", "await_resume"}; MultiExprArg Args[] = {None, CoroHandle, None}; for (size_t I = 0, N = llvm::array_lengthof(Funcs); I != N; ++I) { ExprResult Result = buildMemberCall(S, Operand, Loc, Funcs[I], Args[I]); if (Result.isInvalid()) return Calls; Calls.Results[I] = Result.get(); } // Assume the calls are valid; all further checking should make them invalid. Calls.IsInvalid = false; using ACT = ReadySuspendResumeResult::AwaitCallType; CallExpr *AwaitReady = cast<CallExpr>(Calls.Results[ACT::ACT_Ready]); if (!AwaitReady->getType()->isDependentType()) { // [expr.await]p3 [...] // — await-ready is the expression e.await_ready(), contextually converted // to bool. ExprResult Conv = S.PerformContextuallyConvertToBool(AwaitReady); if (Conv.isInvalid()) { S.Diag(AwaitReady->getDirectCallee()->getLocStart(), diag::note_await_ready_no_bool_conversion); S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required) << AwaitReady->getDirectCallee() << E->getSourceRange(); Calls.IsInvalid = true; } Calls.Results[ACT::ACT_Ready] = Conv.get(); } CallExpr *AwaitSuspend = cast<CallExpr>(Calls.Results[ACT::ACT_Suspend]); if (!AwaitSuspend->getType()->isDependentType()) { // [expr.await]p3 [...] // - await-suspend is the expression e.await_suspend(h), which shall be // a prvalue of type void or bool. QualType RetType = AwaitSuspend->getType(); if (RetType != S.Context.BoolTy && RetType != S.Context.VoidTy) { S.Diag(AwaitSuspend->getCalleeDecl()->getLocation(), diag::err_await_suspend_invalid_return_type) << RetType; S.Diag(Loc, diag::note_coroutine_promise_call_implicitly_required) << AwaitSuspend->getDirectCallee(); Calls.IsInvalid = true; } } return Calls; }
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); } }
void QGetEnv::VisitStmt(clang::Stmt *stmt) { // 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) CXXMemberCallExpr *memberCall = dyn_cast<CXXMemberCallExpr>(stmt); if (!memberCall) return; CXXMethodDecl *method = memberCall->getMethodDecl(); if (!method) return; CXXRecordDecl *record = method->getParent(); if (!record || record->getNameAsString() != "QByteArray") { return; } std::vector<CallExpr *> calls = Utils::callListForChain(memberCall); if (calls.size() != 2) return; CallExpr *qgetEnvCall = calls.back(); FunctionDecl *func = qgetEnvCall->getDirectCallee(); if (!func || func->getNameAsString() != "qgetenv") return; string methodname = method->getNameAsString(); string errorMsg; std::string replacement; if (methodname == "isEmpty") { errorMsg = "qgetenv().isEmpty() allocates."; replacement = "qEnvironmentVariableIsEmpty"; } else if (methodname == "isNull") { errorMsg = "qgetenv().isNull() allocates."; replacement = "qEnvironmentVariableIsSet"; } else if (methodname == "toInt") { errorMsg = "qgetenv().toInt() is slow."; replacement = "qEnvironmentVariableIntValue"; } if (!errorMsg.empty()) { std::vector<FixItHint> fixits; if (isFixitEnabled(FixitAll)) { const bool success = FixItUtils::transformTwoCallsIntoOne(m_ci, qgetEnvCall, memberCall, replacement, fixits); if (!success) { queueManualFixitWarning(memberCall->getLocStart(), FixitAll); } } errorMsg += " Use " + replacement + "() instead"; emitWarning(memberCall->getLocStart(), errorMsg.c_str(), fixits); } }
void Inliner::VisitBinaryOperator(BinaryOperator * node) { Expr * lhs = node->getLHS(), * rhs = node->getRHS(); CallExpr * call = dyn_cast<CallExpr>(rhs); if (call && call->getDirectCallee()->isThisDeclarationADefinition()) { // replace: x = foo(y); with: /*x = */ { //inlined foo } Rewriter &rewriter = (current_func_->isMain()) ? main_rewriter_ : rewriter_; rewriter.InsertText(lhs->getLocStart(),"/*"); rewriter.InsertText(rhs->getLocStart(),"*/"); } return_vars_.push_back(Utils::PrintStmt(lhs,contex_)); VisitChildren(node); return_vars_.pop_back(); }
void QStringAllocations::VisitFromLatin1OrUtf8(Stmt *stmt) { CallExpr *callExpr = dyn_cast<CallExpr>(stmt); if (!callExpr) return; FunctionDecl *functionDecl = callExpr->getDirectCallee(); if (!StringUtils::functionIsOneOf(functionDecl, {"fromLatin1", "fromUtf8"})) return; CXXMethodDecl *methodDecl = dyn_cast<CXXMethodDecl>(functionDecl); if (!StringUtils::isOfClass(methodDecl, "QString")) return; if (!Utils::callHasDefaultArguments(callExpr) || !hasCharPtrArgument(functionDecl, 2)) // QString::fromLatin1("foo", 1) is ok return; if (!containsStringLiteralNoCallExpr(callExpr)) return; if (!isOptionSet("no-msvc-compat")) { StringLiteral *lt = stringLiteralForCall(callExpr); if (lt && lt->getNumConcatenated() > 1) { return; // Nothing to do here, MSVC doesn't like it } } vector<ConditionalOperator*> ternaries; HierarchyUtils::getChilds(callExpr, ternaries, 2); if (!ternaries.empty()) { auto ternary = ternaries[0]; if (Utils::ternaryOperatorIsOfStringLiteral(ternary)) { emitWarning(stmt->getLocStart(), string("QString::fromLatin1() being passed a literal")); } return; } std::vector<FixItHint> fixits; if (isFixitEnabled(FromLatin1_FromUtf8Allocations)) { const FromFunction fromFunction = functionDecl->getNameAsString() == "fromLatin1" ? FromLatin1 : FromUtf8; fixits = fixItReplaceFromLatin1OrFromUtf8(callExpr, fromFunction); } if (functionDecl->getNameAsString() == "fromLatin1") { emitWarning(stmt->getLocStart(), string("QString::fromLatin1() being passed a literal"), fixits); } else { emitWarning(stmt->getLocStart(), string("QString::fromUtf8() being passed a literal"), fixits); } }
// true for: QString::fromLatin1().arg() // false for: QString::fromLatin1("") // true for: QString s = QString::fromLatin1("foo") // false for: s += QString::fromLatin1("foo"), etc. static bool isQStringLiteralCandidate(Stmt *s, ParentMap *map, const LangOptions &lo, const SourceManager &sm , int currentCall = 0) { if (!s) return false; MemberExpr *memberExpr = dyn_cast<MemberExpr>(s); if (memberExpr) return true; auto constructExpr = dyn_cast<CXXConstructExpr>(s); if (StringUtils::isOfClass(constructExpr, "QString")) return true; if (Utils::isAssignOperator(dyn_cast<CXXOperatorCallExpr>(s), "QString", "QLatin1String", lo)) return true; if (Utils::isAssignOperator(dyn_cast<CXXOperatorCallExpr>(s), "QString", "QString", lo)) return true; CallExpr *callExpr = dyn_cast<CallExpr>(s); StringLiteral *literal = stringLiteralForCall(callExpr); auto operatorCall = dyn_cast<CXXOperatorCallExpr>(s); if (operatorCall && StringUtils::returnTypeName(operatorCall, lo) != "QTestData") { // QTest::newRow will static_assert when using QLatin1String // Q_STATIC_ASSERT_X(QMetaTypeId2<T>::Defined, "Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system"); string className = StringUtils::classNameFor(operatorCall); if (className == "QString") { return false; } else if (className.empty() && StringUtils::hasArgumentOfType(operatorCall->getDirectCallee(), "QString", lo)) { return false; } } if (currentCall > 0 && callExpr) { auto fDecl = callExpr->getDirectCallee(); if (fDecl && betterTakeQLatin1String(dyn_cast<CXXMethodDecl>(fDecl), literal)) return false; return true; } if (currentCall == 0 || dyn_cast<ImplicitCastExpr>(s) || dyn_cast<CXXBindTemporaryExpr>(s) || dyn_cast<MaterializeTemporaryExpr>(s)) // skip this cruft return isQStringLiteralCandidate(HierarchyUtils::parent(map, s), map, lo, sm, currentCall + 1); return false; }
// Deal with all the call expr in the transaction. bool VisitCallExpr(CallExpr* TheCall) { if (FunctionDecl* FDecl = TheCall->getDirectCallee()) { std::bitset<32> ArgIndexs; for (FunctionDecl::redecl_iterator RI = FDecl->redecls_begin(), RE = FDecl->redecls_end(); RI != RE; ++RI) { for (specific_attr_iterator<NonNullAttr> I = RI->specific_attr_begin<NonNullAttr>(), E = RI->specific_attr_end<NonNullAttr>(); I != E; ++I) { NonNullAttr *NonNull = *I; // Store all the null attr argument's index into "ArgIndexs". for (NonNullAttr::args_iterator i = NonNull->args_begin(), e = NonNull->args_end(); i != e; ++i) { // Get the argument with the nonnull attribute. const Expr* Arg = TheCall->getArg(*i); // Check whether we can get the argument'value. If the argument is // not null, then ignore this argument and continue to deal with the // next argument with the nonnull attribute.ArgIndexs.set(*i); bool Result; ASTContext& Context = m_Sema->getASTContext(); if (Arg->EvaluateAsBooleanCondition(Result, Context) && Result) { continue; } ArgIndexs.set(*i); } break; } } if (ArgIndexs.any()) { // Get the function decl's name. std::string FName = getMangledName(FDecl); // Store the function decl's name into the vector. m_NonNullDeclNames.push_back(FName); // Store the function decl's name with its null attr args' indexes // into the map. m_NonNullArgIndexs.insert(std::make_pair(FName, ArgIndexs)); } // FIXME: For now we will only work/check on declarations that are not // deserialized. We want to avoid our null deref transaformer to // deserialize all the contents of a PCH/PCM. // We have to think of a better way to find the annotated // declarations, without that to cause too much deserialization. Stmt* S = (FDecl->isFromASTFile()) ? 0 : FDecl->getBody(); if (S) { for (Stmt::child_iterator II = S->child_begin(), EE = S->child_end(); II != EE; ++II) { CallExpr* child = dyn_cast<CallExpr>(*II); if (child && child->getDirectCallee() != FDecl) VisitCallExpr(child); } } } return true; // returning false will abort the in-depth traversal. }
/* 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; } } } }