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