-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[webkit.UncountedLambdaCapturesChecker] Ignore trivial functions and [[clang::noescape]]. #114897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
d08c1b3
e9da7f9
05ce9ff
fcd823b
3cd6678
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "ASTUtils.h" | ||
| #include "DiagOutputUtils.h" | ||
| #include "PtrTypesSemantics.h" | ||
| #include "clang/AST/CXXInheritance.h" | ||
|
|
@@ -26,6 +27,7 @@ class UncountedLambdaCapturesChecker | |
| BugType Bug{this, "Lambda capture of uncounted variable", | ||
| "WebKit coding guidelines"}; | ||
| mutable BugReporter *BR = nullptr; | ||
| TrivialFunctionAnalysis TFA; | ||
|
|
||
| public: | ||
| void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, | ||
|
|
@@ -37,6 +39,11 @@ class UncountedLambdaCapturesChecker | |
| // want to visit those, so we make our own RecursiveASTVisitor. | ||
| struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> { | ||
| const UncountedLambdaCapturesChecker *Checker; | ||
| llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore; | ||
| QualType ClsType; | ||
|
|
||
| using Base = RecursiveASTVisitor<LocalVisitor>; | ||
|
|
||
| explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker) | ||
| : Checker(Checker) { | ||
| assert(Checker); | ||
|
|
@@ -45,32 +52,119 @@ class UncountedLambdaCapturesChecker | |
| bool shouldVisitTemplateInstantiations() const { return true; } | ||
| bool shouldVisitImplicitCode() const { return false; } | ||
|
|
||
| bool VisitLambdaExpr(LambdaExpr *L) { | ||
| Checker->visitLambdaExpr(L); | ||
| bool TraverseDecl(Decl *D) { | ||
| if (auto *CXXMD = dyn_cast<CXXMethodDecl>(D)) { | ||
| llvm::SaveAndRestore SavedDecl(ClsType); | ||
| ClsType = CXXMD->getThisType(); | ||
| return Base::TraverseDecl(D); | ||
| } | ||
| return Base::TraverseDecl(D); | ||
| } | ||
|
|
||
| bool shouldCheckThis() { | ||
| auto result = !ClsType.isNull() ? isUnsafePtr(ClsType) : std::nullopt; | ||
| return result && *result; | ||
| } | ||
|
|
||
| bool VisitDeclRefExpr(DeclRefExpr *DRE) { | ||
| if (DeclRefExprsToIgnore.contains(DRE)) | ||
| return true; | ||
| auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl()); | ||
| if (!VD) | ||
| return true; | ||
| auto *Init = VD->getInit(); | ||
| if (!Init) | ||
| return true; | ||
| auto *L = dyn_cast_or_null<LambdaExpr>(Init->IgnoreParenCasts()); | ||
| if (!L) | ||
| return true; | ||
| Checker->visitLambdaExpr(L, shouldCheckThis()); | ||
| return true; | ||
| } | ||
|
|
||
| // WTF::switchOn(T, F... f) is a variadic template function and couldn't | ||
| // be annotated with NOESCAPE. We hard code it here to workaround that. | ||
|
Comment on lines
+82
to
+83
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ooo interesting. Technically there's standardized syntax for this sort of stuff: https://en.cppreference.com/w/cpp/language/parameter_pack
But, IIUC, this requires the attribute to be, like, a function attribute that receives the parameter identifier as an attribute argument(?) So it'll probably not work out of the box but it's probably doable. If you run into many more functions of that nature, this could be a way out.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. It's probably an overkill for this one function but I'd keep that in my mind. |
||
| bool shouldTreatAllArgAsNoEscape(FunctionDecl *Decl) { | ||
| auto *NsDecl = Decl->getParent(); | ||
| if (!NsDecl || !isa<NamespaceDecl>(NsDecl)) | ||
| return false; | ||
| return safeGetName(NsDecl) == "WTF" && safeGetName(Decl) == "switchOn"; | ||
| } | ||
|
|
||
| bool VisitCallExpr(CallExpr *CE) { | ||
| checkCalleeLambda(CE); | ||
| if (auto *Callee = CE->getDirectCallee()) { | ||
| bool TreatAllArgsAsNoEscape = shouldTreatAllArgAsNoEscape(Callee); | ||
| unsigned ArgIndex = 0; | ||
| for (auto *Param : Callee->parameters()) { | ||
| if (ArgIndex >= CE->getNumArgs()) | ||
| break; | ||
|
||
| auto *Arg = CE->getArg(ArgIndex)->IgnoreParenCasts(); | ||
| if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape) { | ||
| if (auto *L = dyn_cast_or_null<LambdaExpr>(Arg)) { | ||
| Checker->visitLambdaExpr(L, shouldCheckThis()); | ||
| } | ||
| } | ||
| ++ArgIndex; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| void checkCalleeLambda(CallExpr *CE) { | ||
| auto *Callee = CE->getCallee(); | ||
| if (!Callee) | ||
| return; | ||
| auto *DRE = dyn_cast<DeclRefExpr>(Callee->IgnoreParenCasts()); | ||
| if (!DRE) | ||
| return; | ||
| auto *MD = dyn_cast_or_null<CXXMethodDecl>(DRE->getDecl()); | ||
| if (!MD || CE->getNumArgs() != 1) | ||
| return; | ||
| auto *Arg = CE->getArg(0)->IgnoreParenCasts(); | ||
| auto *ArgRef = dyn_cast<DeclRefExpr>(Arg); | ||
| if (!ArgRef) | ||
| return; | ||
| auto *VD = dyn_cast_or_null<VarDecl>(ArgRef->getDecl()); | ||
| if (!VD) | ||
| return; | ||
| auto *Init = VD->getInit()->IgnoreParenCasts(); | ||
| auto *L = dyn_cast_or_null<LambdaExpr>(Init); | ||
| if (!L) | ||
| return; | ||
| DeclRefExprsToIgnore.insert(ArgRef); | ||
| Checker->visitLambdaExpr(L, shouldCheckThis(), | ||
| /* ignoreParamVarDecl */ true); | ||
| } | ||
| }; | ||
|
|
||
| LocalVisitor visitor(this); | ||
| visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); | ||
| } | ||
|
|
||
| void visitLambdaExpr(LambdaExpr *L) const { | ||
| void visitLambdaExpr(LambdaExpr *L, bool shouldCheckThis, | ||
| bool ignoreParamVarDecl = false) const { | ||
| if (TFA.isTrivial(L->getBody())) | ||
| return; | ||
| for (const LambdaCapture &C : L->captures()) { | ||
| if (C.capturesVariable()) { | ||
| ValueDecl *CapturedVar = C.getCapturedVar(); | ||
| if (ignoreParamVarDecl && isa<ParmVarDecl>(CapturedVar)) | ||
| continue; | ||
| QualType CapturedVarQualType = CapturedVar->getType(); | ||
| if (auto *CapturedVarType = CapturedVarQualType.getTypePtrOrNull()) { | ||
| auto IsUncountedPtr = isUncountedPtr(CapturedVarQualType); | ||
| if (IsUncountedPtr && *IsUncountedPtr) | ||
| reportBug(C, CapturedVar, CapturedVarType); | ||
| } | ||
| auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType()); | ||
| if (IsUncountedPtr && *IsUncountedPtr) | ||
| reportBug(C, CapturedVar, CapturedVarQualType); | ||
| } else if (C.capturesThis() && shouldCheckThis) { | ||
| if (ignoreParamVarDecl) // this is always a parameter to this function. | ||
| continue; | ||
| reportBugOnThisPtr(C); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar, | ||
| const Type *T) const { | ||
| const QualType T) const { | ||
| assert(CapturedVar); | ||
|
|
||
| SmallString<100> Buf; | ||
|
|
@@ -89,7 +183,25 @@ class UncountedLambdaCapturesChecker | |
| } | ||
|
|
||
| printQuotedQualifiedName(Os, Capture.getCapturedVar()); | ||
| Os << " to uncounted type is unsafe."; | ||
| Os << " to ref-counted / CheckedPtr capable type is unsafe."; | ||
|
|
||
| PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager()); | ||
| auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); | ||
| BR->emitReport(std::move(Report)); | ||
| } | ||
|
|
||
| void reportBugOnThisPtr(const LambdaCapture &Capture) const { | ||
| SmallString<100> Buf; | ||
| llvm::raw_svector_ostream Os(Buf); | ||
|
|
||
| if (Capture.isExplicit()) { | ||
| Os << "Captured "; | ||
| } else { | ||
| Os << "Implicitly captured "; | ||
| } | ||
|
|
||
| Os << "raw-pointer 'this' to ref-counted / CheckedPtr capable type is " | ||
|
||
| "unsafe."; | ||
|
|
||
| PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager()); | ||
| auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.