Expand the smartpointerderefaddress compiler plugin to more ptr types

Instead of having a hard-coded list of smart pointer types, the plugin
now checks if the called type has a method called “get” which returns a
pointer with the same type as the overall expression. The intention is
that this should be general enough to catch all smart pointers.

Change-Id: I8c6f21f34a9debb81765155c544d0564c0f8ec58
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211848
Reviewed-by: Neil Roberts <nroberts@libreoffice.org>
Tested-by: Jenkins
This commit is contained in:
Neil Roberts
2026-10-07 19:16:39 +02:00
parent 99f157a04f
commit 32e937ae2d
2 changed files with 51 additions and 5 deletions
@@ -31,6 +31,9 @@
* See tdf#173585 for an example. * See tdf#173585 for an example.
* *
* Either way x.get() is easier to understand. * Either way x.get() is easier to understand.
*
* Whether the object is a smart pointer or not is determined by whether it has a get() method which
* returns the same type as the original expression.
*/ */
namespace namespace
@@ -52,6 +55,37 @@ public:
bool VisitUnaryOperator(UnaryOperator const*); bool VisitUnaryOperator(UnaryOperator const*);
}; };
bool cxxRecordHasGetMethod(const CXXRecordDecl* recordDecl, const QualType& expressionType)
{
for (auto method : recordDecl->methods())
{
if (!method->isInstance() || method->param_size() != 0
|| !loplugin::DeclCheck(method).Function("get"))
{
continue;
}
QualType returnType = method->getReturnType().getUnqualifiedType().getCanonicalType();
if (returnType == expressionType)
return true;
}
return false;
}
bool cxxRecordOrBaseHasGetMethod(const CXXRecordDecl* recordDecl, const QualType& expressionType)
{
if (cxxRecordHasGetMethod(recordDecl, expressionType))
return true;
auto baseCallback = [&](const CXXRecordDecl* baseRecordDecl) {
return !cxxRecordHasGetMethod(baseRecordDecl, expressionType);
};
return !recordDecl->forallBases(baseCallback);
}
bool SmartPointerDerefAddress::VisitUnaryOperator(UnaryOperator const* unaryOperator) bool SmartPointerDerefAddress::VisitUnaryOperator(UnaryOperator const* unaryOperator)
{ {
if (ignoreLocation(unaryOperator)) if (ignoreLocation(unaryOperator))
@@ -65,12 +99,23 @@ bool SmartPointerDerefAddress::VisitUnaryOperator(UnaryOperator const* unaryOper
if (!innerOp || innerOp->getOperator() != OO_Star) if (!innerOp || innerOp->getOperator() != OO_Star)
return true; return true;
auto const tc = loplugin::TypeCheck(innerOp->getArg(0)->getType()); const Expr* derefedExpr = innerOp->getArg(0);
if (tc.ClassOrStruct("shared_ptr").StdNamespace() // Remove any implicit casts so we can get the type of the original expression, not the one
|| tc.ClassOrStruct("__shared_ptr_access").StdNamespace() // where operator* is defined
|| tc.ClassOrStruct("unique_ptr").StdNamespace() while (const ImplicitCastExpr* castExpr = dyn_cast<ImplicitCastExpr>(derefedExpr))
|| tc.ClassOrStruct("UnoCursorPointer").Namespace("sw").GlobalNamespace()) derefedExpr = castExpr->getSubExpr();
// Check if the dereferenced type is a struct or a class that also has a get method that returns
// the same type as the whole expression
const CXXRecordDecl* recordDecl = derefedExpr->getType()->getAsCXXRecordDecl();
if (!recordDecl)
return true;
QualType expressionType = unaryOperator->getType().getUnqualifiedType().getCanonicalType();
if (cxxRecordOrBaseHasGetMethod(recordDecl, expressionType))
{ {
report(DiagnosticsEngine::Warning, report(DiagnosticsEngine::Warning,
"'*' followed by '&' operating on %0, rather use '.get()'", "'*' followed by '&' operating on %0, rather use '.get()'",
@@ -18,6 +18,7 @@ class UnoCursorPointer
{ {
public: public:
int& operator*() { return *m_pInteger; }; int& operator*() { return *m_pInteger; };
int* get() { return m_pInteger; };
private: private:
int* m_pInteger; int* m_pInteger;