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.
4  *
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  */
9 
10 #ifndef LO_CLANG_SHARED_PLUGINS
11 
12 #include "check.hxx"
13 #include "plugin.hxx"
14 
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".)
22 
23 namespace
24 {
25 class ClassMemAccess final : public loplugin::FilteringPlugin<ClassMemAccess>
26 {
27 public:
ClassMemAccess(loplugin::InstantiationData const & data)28     explicit ClassMemAccess(loplugin::InstantiationData const& data)
29         : FilteringPlugin(data)
30     {
31     }
32 
VisitCallExpr(CallExpr const * expr)33     bool VisitCallExpr(CallExpr const* expr)
34     {
35         if (ignoreLocation(expr))
36         {
37             return true;
38         }
39         auto const fdecl = expr->getDirectCallee();
40         if (fdecl == nullptr)
41         {
42             return true;
43         }
44         auto dc = loplugin::DeclCheck(fdecl).Function("memset");
45         if (!(dc.GlobalNamespace() || dc.StdNamespace()))
46         {
47             return true;
48         }
49         if (expr->getNumArgs() != 3)
50         {
51             if (isDebugMode())
52             {
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();
58             }
59             return true;
60         }
61         auto e = expr->getArg(0)->IgnoreParenImpCasts();
62         while (auto const cast = dyn_cast<ExplicitCastExpr>(e))
63         {
64             if (!loplugin::TypeCheck(cast->getTypeAsWritten()).Pointer().Void())
65             {
66                 break;
67             }
68             e = cast->getSubExprAsWritten()->IgnoreParenImpCasts();
69         }
70         QualType t;
71         if (auto const t1 = e->getType()->getAs<clang::PointerType>())
72         {
73             t = t1->getPointeeType();
74         }
75         else if (e->getType()->isArrayType())
76         {
77             t = e->getType();
78             while (auto const t2 = t->getAsArrayTypeUnsafe())
79             {
80                 t = t2->getElementType();
81             }
82         }
83         else
84         {
85             if (isDebugMode())
86             {
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();
93             }
94             return true;
95         }
96         auto const decl = t->getAsCXXRecordDecl();
97         if (decl == nullptr)
98         {
99             return true;
100         }
101         if (!decl->isCompleteDefinition())
102         {
103             return true; // conservatively assume it may be trivial
104         }
105         if (decl->isTrivial())
106         {
107             return true;
108         }
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;
114     }
115 
preRun()116     bool preRun() override { return compiler.getLangOpts().CPlusPlus; }
117 
118 private:
run()119     void run() override
120     {
121         if (preRun())
122         {
123             TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
124         }
125     }
126 };
127 
128 loplugin::Plugin::Registration<ClassMemAccess> classmemaccess("classmemaccess");
129 }
130 
131 #endif
132 
133 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
134