bump product version to 6.4.0.3
[LibreOffice.git] / compilerplugins / clang / classmemaccess.cxx
blob40132c36e1a5002ae92e337c172025764deb52b2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
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/.
8 */
10 #ifndef LO_CLANG_SHARED_PLUGINS
12 #include "check.hxx"
13 #include "plugin.hxx"
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".)
23 namespace
25 class ClassMemAccess final : public loplugin::FilteringPlugin<ClassMemAccess>
27 public:
28 explicit ClassMemAccess(loplugin::InstantiationData const& data)
29 : FilteringPlugin(data)
33 bool VisitCallExpr(CallExpr const* expr)
35 if (ignoreLocation(expr))
37 return true;
39 auto const fdecl = expr->getDirectCallee();
40 if (fdecl == nullptr)
42 return true;
44 auto dc = loplugin::DeclCheck(fdecl).Function("memset");
45 if (!(dc.GlobalNamespace() || dc.StdNamespace()))
47 return true;
49 if (expr->getNumArgs() != 3)
51 if (isDebugMode())
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();
59 return true;
61 auto e = expr->getArg(0)->IgnoreParenImpCasts();
62 while (auto const cast = dyn_cast<ExplicitCastExpr>(e))
64 if (!loplugin::TypeCheck(cast->getTypeAsWritten()).Pointer().Void())
66 break;
68 e = cast->getSubExprAsWritten()->IgnoreParenImpCasts();
70 QualType t;
71 if (auto const t1 = e->getType()->getAs<clang::PointerType>())
73 t = t1->getPointeeType();
75 else if (e->getType()->isArrayType())
77 t = e->getType();
78 while (auto const t2 = t->getAsArrayTypeUnsafe())
80 t = t2->getElementType();
83 else
85 if (isDebugMode())
87 report(DiagnosticsEngine::Fatal,
88 "unexpected call to %0 with first argument of non-pointer type %1",
89 expr->getExprLoc())
90 << fdecl << e->getType() << expr->getSourceRange();
91 report(DiagnosticsEngine::Note, "%0 declared here", fdecl->getLocation())
92 << fdecl << fdecl->getSourceRange();
94 return true;
96 auto const decl = t->getAsCXXRecordDecl();
97 if (decl == nullptr)
99 return true;
101 if (!decl->isCompleteDefinition())
103 return true; // conservatively assume it may be trivial
105 if (decl->isTrivial())
107 return true;
109 report(DiagnosticsEngine::Warning,
110 "%0 writing to an object of non-trivial type %1; use assignment instead",
111 expr->getExprLoc())
112 << fdecl << decl << expr->getSourceRange();
113 return true;
116 bool preRun() override { return compiler.getLangOpts().CPlusPlus; }
118 private:
119 void run() override
121 if (preRun())
123 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
128 loplugin::Plugin::Registration<ClassMemAccess> classmemaccess("classmemaccess");
131 #endif
133 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */