Version 24.2.2.2, tag libreoffice-24.2.2.2
[LibreOffice.git] / compilerplugins / clang / typedefparam.cxx
blob6fffe66c8483385efa2acde48a0e7a8e5a32ad83
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <cassert>
13 #include <string>
14 #include <iostream>
15 #include <fstream>
16 #include <set>
18 #include "config_clang.h"
20 #include "check.hxx"
21 #include "plugin.hxx"
23 /**
24 Check that parameters at the declaration site and the definition site are the same.
25 This can be important when refactoring, and can sometimes make a difference when compiling
26 for platforms with different pointer-sizes (e.g. 32 vs 64 bit)
28 namespace
30 class TypedefParam : public loplugin::FilteringRewritePlugin<TypedefParam>
32 public:
33 explicit TypedefParam(loplugin::InstantiationData const& data)
34 : FilteringRewritePlugin(data)
38 void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
40 bool VisitFunctionDecl(FunctionDecl const*);
41 bool VisitCXXMethodDecl(CXXMethodDecl const*);
44 static bool areTypesEqual(QualType lhs, QualType rhs);
46 bool TypedefParam::VisitFunctionDecl(FunctionDecl const* functionDecl)
48 if (ignoreLocation(functionDecl))
49 return true;
50 if (functionDecl->isFunctionTemplateSpecialization())
51 return true;
52 auto canonicalDecl = functionDecl->getCanonicalDecl();
53 if (canonicalDecl == functionDecl)
54 return true;
56 for (unsigned i = 0; i < functionDecl->getNumParams(); ++i)
58 ParmVarDecl const* thisParam = functionDecl->getParamDecl(i);
59 ParmVarDecl const* canonicalParam = canonicalDecl->getParamDecl(i);
60 if (!areTypesEqual(thisParam->getType(), canonicalParam->getType()))
62 #if defined _WIN32
63 // SAL_IMPLEMENT_MAIN (include/sal/main.h) declares the third parameter of WinMain to be
64 // of type 'char *' rather than 'LPSTR', but using that typedef there would require
65 // including windows.h, which would require including include/prewin.h and
66 // include/postwin.h (to undo macros like Yield defined in windows.h) but which (unlike
67 // include/sal/main.h) are not part of the stable URE interface; so just ignore that
68 // here:
69 if (loplugin::DeclCheck(functionDecl).Function("WinMain").GlobalNamespace())
71 continue;
73 #endif
74 report(DiagnosticsEngine::Warning,
75 "function param %0 at definition site does not match function param at "
76 "declaration site, %1 vs %2",
77 thisParam->getLocation())
78 << (i + 1) << thisParam->getType() << canonicalParam->getType()
79 << functionDecl->getSourceRange();
80 report(DiagnosticsEngine::Note, "declaration site here", canonicalParam->getLocation())
81 << canonicalDecl->getSourceRange();
85 if (!areTypesEqual(functionDecl->getReturnType(), canonicalDecl->getReturnType()))
87 report(DiagnosticsEngine::Warning,
88 "function return type at definition site does not match function param at "
89 "declaration site, %0 vs %1",
90 functionDecl->getBeginLoc())
91 << functionDecl->getReturnType() << canonicalDecl->getReturnType()
92 << functionDecl->getSourceRange();
93 report(DiagnosticsEngine::Note, "declaration site here", canonicalDecl->getBeginLoc())
94 << canonicalDecl->getSourceRange();
96 return true;
99 bool TypedefParam::VisitCXXMethodDecl(CXXMethodDecl const* methodDecl)
101 if (ignoreLocation(methodDecl))
102 return true;
103 if (methodDecl->isFunctionTemplateSpecialization())
104 return true;
105 // only warn on the declaration site
106 if (methodDecl->getCanonicalDecl() != methodDecl)
107 return true;
109 StringRef aFileName = getFilenameOfLocation(
110 compiler.getSourceManager().getSpellingLoc(methodDecl->getBeginLoc()));
111 // seems to be using typedefs as a form of documentation for method params
112 if (loplugin::hasPathnamePrefix(aFileName, SRCDIR "/sw/source/filter/ww8/ww8scan.hxx"))
113 return true;
114 // necessary to work around changes in popplers API
115 if (loplugin::hasPathnamePrefix(aFileName, SRCDIR
116 "/sdext/source/pdfimport/xpdfwrapper/pdfioutdev_gpl.hxx"))
117 return true;
119 for (auto superMethodIt = methodDecl->begin_overridden_methods();
120 superMethodIt != methodDecl->end_overridden_methods(); ++superMethodIt)
122 const CXXMethodDecl* superMethodDecl = *superMethodIt;
123 int i = 0;
124 for (const ParmVarDecl* superParmVarDecl : superMethodDecl->parameters())
126 const ParmVarDecl* parmVarDecl = methodDecl->getParamDecl(i);
127 if (!areTypesEqual(parmVarDecl->getType(), superParmVarDecl->getType()))
129 report(DiagnosticsEngine::Warning,
130 "method param %0 does not match overridden method param "
131 "%1 vs %2",
132 parmVarDecl->getLocation())
133 << (i + 1) << parmVarDecl->getType() << superParmVarDecl->getType()
134 << methodDecl->getSourceRange();
135 report(DiagnosticsEngine::Note, "super-class method here",
136 superParmVarDecl->getLocation())
137 << superMethodDecl->getSourceRange();
139 ++i;
142 // it is quite normal to override a method and return a more specific pointer/reference type
143 auto returnType = methodDecl->getReturnType();
144 if (!returnType->isPointerType() && !returnType->isReferenceType())
145 if (!areTypesEqual(returnType, superMethodDecl->getReturnType()))
147 report(DiagnosticsEngine::Warning,
148 "method return type does not match overridden method "
149 "%0 vs %1",
150 methodDecl->getBeginLoc())
151 << methodDecl->getReturnType() << superMethodDecl->getReturnType()
152 << methodDecl->getSourceRange();
153 report(DiagnosticsEngine::Note, "super-class method here",
154 superMethodDecl->getBeginLoc())
155 << superMethodDecl->getSourceRange();
158 return true;
161 static bool areTypesEqual(QualType lhs, QualType rhs)
163 if (lhs == rhs)
164 return true;
166 // ignore template stuff
167 if (lhs->isDependentType() || rhs->isDependentType())
168 return true;
169 // there are some places, e.g. chart2/source/controller/chartapiwrapper/WrappedSymbolProperties.cxx
170 // where just unwrapping these types does not work, so just ignore them
171 if (isa<SubstTemplateTypeParmType>(lhs) || isa<SubstTemplateTypeParmType>(rhs))
172 return true;
174 auto lhsType = lhs.getTypePtr();
175 auto rhsType = rhs.getTypePtr();
177 // this is the carve-out exception for the "typedef struct S {...} T" idiom we use in the UNO code
178 if (auto lhsPointer = dyn_cast<clang::PointerType>(lhsType))
180 if (auto rhsPointer = dyn_cast<clang::PointerType>(rhsType))
182 auto extractRecordType = [](clang::QualType type) {
183 auto recordType = dyn_cast<RecordType>(type);
184 if (recordType)
185 return recordType;
186 auto elaboratedType = dyn_cast<ElaboratedType>(type);
187 if (!elaboratedType)
188 return static_cast<const clang::RecordType*>(nullptr);
189 return dyn_cast<RecordType>(elaboratedType->desugar());
191 auto containsTypedefToRecord = [](clang::QualType type, RecordType const* recordType) {
192 TypedefType const* typedefType = type->getAs<TypedefType>();
193 if (!typedefType)
194 return false;
195 auto tmp = typedefType->desugar();
196 if (auto elaboratedType = dyn_cast<ElaboratedType>(tmp))
197 tmp = elaboratedType->desugar();
198 return tmp.getTypePtr() == recordType;
200 if (auto recordType = extractRecordType(lhsPointer->getPointeeType()))
201 if (containsTypedefToRecord(rhsPointer->getPointeeType(), recordType))
202 return true;
203 if (auto recordType = extractRecordType(rhsPointer->getPointeeType()))
204 if (containsTypedefToRecord(lhsPointer->getPointeeType(), recordType))
205 return true;
209 if (auto lhsElaborated = dyn_cast<ElaboratedType>(lhsType))
211 return areTypesEqual(lhsElaborated->getNamedType(), rhs);
213 if (auto rhsElaborated = dyn_cast<ElaboratedType>(rhsType))
215 return areTypesEqual(lhs, rhsElaborated->getNamedType());
217 if (auto lhsTemplate = dyn_cast<TemplateSpecializationType>(lhsType))
219 return areTypesEqual(lhsTemplate->desugar(), rhs);
221 if (auto rhsTemplate = dyn_cast<TemplateSpecializationType>(rhsType))
223 return areTypesEqual(lhs, rhsTemplate->desugar());
226 if (auto lhsLValue = dyn_cast<LValueReferenceType>(lhsType))
228 auto rhsLValue = dyn_cast<LValueReferenceType>(rhsType);
229 if (!rhsLValue)
230 return false;
231 return areTypesEqual(lhsLValue->getPointeeType(), rhsLValue->getPointeeType());
233 if (auto lhsRValue = dyn_cast<RValueReferenceType>(lhsType))
235 auto rhsRValue = dyn_cast<RValueReferenceType>(rhsType);
236 if (!rhsRValue)
237 return false;
238 return areTypesEqual(lhsRValue->getPointeeType(), rhsRValue->getPointeeType());
240 if (auto lhsPointer = dyn_cast<clang::PointerType>(lhsType))
242 auto rhsPointer = dyn_cast<clang::PointerType>(rhsType);
243 if (!rhsPointer)
244 return false;
245 return areTypesEqual(lhsPointer->getPointeeType(), rhsPointer->getPointeeType());
247 if (auto lhsTypedef = dyn_cast<TypedefType>(lhsType))
249 auto rhsTypedef = dyn_cast<TypedefType>(rhsType);
250 if (!rhsTypedef)
251 return false;
252 // comparing the underlying Decl's here doesn't work, they are not unique
253 if (lhsTypedef->getDecl()->getName() != rhsTypedef->getDecl()->getName())
254 return false;
255 #if defined __APPLE__
256 // My Clang trunk .../lib/clang/9.0.0/include/stddef.h has a
258 // typedef long unsigned int size_t;
260 // while /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/
261 // SDKs/MacOSX10.14.sdk/usr/include/sys/_types/_size_t.h has a
263 // typedef __darwin_size_t size_t;
265 // where __darwin_size_t is a typedef for long unsigned int, too, so that, depending on the
266 // order in which those two files get included, either of those two typedefs can act as a
267 // redeclaration of the other one. However, areTypesEqual would unhelpfully consider such
268 // different occurrences of size_t to be non-equal, so filter them out here. And, at least
269 // with my libcxx trunk .../include/c++/v1/cstddef, std::size_t is a using declaration that
270 // brings size_t from the global namespace into namespace std, so that the above checks for
271 // ElaboratedType sugar will already have unwrapped those to the size_t typedefs in the
272 // global namespace here.
273 if (loplugin::TypeCheck(lhsTypedef).Typedef("size_t").GlobalNamespace()
274 && loplugin::TypeCheck(rhsTypedef).Typedef("size_t").GlobalNamespace())
276 return true;
278 #endif
279 return areTypesEqual(lhsTypedef->desugar(), rhsTypedef->desugar());
281 if (auto lhsTemplate = dyn_cast<TemplateSpecializationType>(lhsType))
283 auto rhsTemplate = dyn_cast<TemplateSpecializationType>(rhsType);
284 if (!rhsTemplate)
285 return false;
286 return areTypesEqual(lhsTemplate->desugar(), rhsTemplate->desugar());
288 if (auto lhsMember = dyn_cast<MemberPointerType>(lhsType))
290 auto rhsMember = dyn_cast<MemberPointerType>(rhsType);
291 if (!rhsMember)
292 return false;
293 if (lhsMember->getClass() != rhsMember->getClass())
294 return true;
295 return areTypesEqual(lhsMember->getPointeeType(), rhsMember->getPointeeType());
297 if (auto lhsParen = dyn_cast<ParenType>(lhsType))
299 auto rhsParen = dyn_cast<ParenType>(rhsType);
300 if (!rhsParen)
301 return false;
302 return areTypesEqual(lhsParen->getInnerType(), rhsParen->getInnerType());
304 if (dyn_cast<FunctionProtoType>(lhsType))
306 auto rhsFunction = dyn_cast<FunctionProtoType>(rhsType);
307 if (!rhsFunction)
308 return false;
309 return true; // TODO
311 if (auto lhsDecayed = dyn_cast<DecayedType>(lhsType))
313 auto rhsDecayed = dyn_cast<DecayedType>(rhsType);
314 if (!rhsDecayed)
315 return false;
316 return areTypesEqual(lhsDecayed->getAdjustedType(), rhsDecayed->getAdjustedType());
318 if (auto lhsAttr = dyn_cast<AttributedType>(lhsType))
320 auto rhsAttr = dyn_cast<AttributedType>(rhsType);
321 if (!rhsAttr)
322 return false;
323 return areTypesEqual(lhsAttr->getModifiedType(), rhsAttr->getModifiedType());
325 return lhsType == rhsType;
328 loplugin::Plugin::Registration<TypedefParam> typedefparam("typedefparam");
330 } // namespace
332 #endif // LO_CLANG_SHARED_PLUGINS
334 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */