1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #ifndef LO_CLANG_SHARED_PLUGINS
15 // Similar to GCC "warning: ‘void* memset(void*, int, size_t)’ writing to an object of non-trivial
16 // type ‘...’; use assignment instead [-Wclass-memaccess]", but looking through toplevel cast to
17 // void* and taking arrays into account in addition to pointers. (Clang has
18 // -Wdynamic-class-memaccess, but that only warns about memset overwriting a vtable pointer. GCC
19 // deliberately does not warn when there is a toplevel cast to void*, see
20 // <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81327> "[8 Regression] cast to void* does not
21 // suppress -Wclass-memaccess".)
25 class ClassMemAccess final
: public loplugin::FilteringPlugin
<ClassMemAccess
>
28 explicit ClassMemAccess(loplugin::InstantiationData
const& data
)
29 : FilteringPlugin(data
)
33 bool VisitCallExpr(CallExpr
const* expr
)
35 if (ignoreLocation(expr
))
39 auto const fdecl
= expr
->getDirectCallee();
44 auto dc
= loplugin::DeclCheck(fdecl
).Function("memset");
45 if (!(dc
.GlobalNamespace() || dc
.StdNamespace()))
49 if (expr
->getNumArgs() != 3)
53 report(DiagnosticsEngine::Fatal
,
54 "unexpected call to %0 with %1 instead of 3 arguments", expr
->getExprLoc())
55 << fdecl
<< expr
->getNumArgs() << expr
->getSourceRange();
56 report(DiagnosticsEngine::Note
, "%0 declared here", fdecl
->getLocation())
57 << fdecl
<< fdecl
->getSourceRange();
61 auto e
= expr
->getArg(0)->IgnoreParenImpCasts();
62 while (auto const cast
= dyn_cast
<ExplicitCastExpr
>(e
))
64 if (!loplugin::TypeCheck(cast
->getTypeAsWritten()).Pointer().Void())
68 e
= cast
->getSubExprAsWritten()->IgnoreParenImpCasts();
71 if (auto const t1
= e
->getType()->getAs
<clang::PointerType
>())
73 t
= t1
->getPointeeType();
75 else if (e
->getType()->isArrayType())
78 while (auto const t2
= t
->getAsArrayTypeUnsafe())
80 t
= t2
->getElementType();
87 report(DiagnosticsEngine::Fatal
,
88 "unexpected call to %0 with first argument of non-pointer type %1",
90 << fdecl
<< e
->getType() << expr
->getSourceRange();
91 report(DiagnosticsEngine::Note
, "%0 declared here", fdecl
->getLocation())
92 << fdecl
<< fdecl
->getSourceRange();
96 auto const decl
= t
->getAsCXXRecordDecl();
101 if (!decl
->isCompleteDefinition())
103 return true; // conservatively assume it may be trivial
105 if (decl
->isTrivial())
109 report(DiagnosticsEngine::Warning
,
110 "%0 writing to an object of non-trivial type %1; use assignment instead",
112 << fdecl
<< decl
<< expr
->getSourceRange();
116 bool preRun() override
{ return compiler
.getLangOpts().CPlusPlus
; }
123 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
128 loplugin::Plugin::Registration
<ClassMemAccess
> classmemaccess("classmemaccess");
133 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */