1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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
22 Check that parameters at the declaration site and the definition site are the same.
23 This can be important when refactoring, and can sometimes make a difference when compiling
24 for platforms with different pointer-sizes (e.g. 32 vs 64 bit)
28 class TypedefParam
: public loplugin::FilteringRewritePlugin
<TypedefParam
>
31 explicit TypedefParam(loplugin::InstantiationData
const& data
)
32 : FilteringRewritePlugin(data
)
36 void run() override
{ TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl()); }
38 bool VisitFunctionDecl(FunctionDecl
const*);
39 bool VisitCXXMethodDecl(CXXMethodDecl
const*);
42 static bool areTypesEqual(QualType lhs
, QualType rhs
);
44 bool TypedefParam::VisitFunctionDecl(FunctionDecl
const* functionDecl
)
46 if (ignoreLocation(functionDecl
))
48 if (functionDecl
->isFunctionTemplateSpecialization())
50 auto canonicalDecl
= functionDecl
->getCanonicalDecl();
51 if (canonicalDecl
== functionDecl
)
54 for (unsigned i
= 0; i
< functionDecl
->getNumParams(); ++i
)
56 ParmVarDecl
const* thisParam
= functionDecl
->getParamDecl(i
);
57 ParmVarDecl
const* canonicalParam
= canonicalDecl
->getParamDecl(i
);
58 if (!areTypesEqual(thisParam
->getType(), canonicalParam
->getType()))
61 // SAL_IMPLEMENT_MAIN (include/sal/main.h) declares the third parameter of WinMain to be
62 // of type 'char *' rather than 'LPSTR', but using that typedef there would require
63 // including windows.h, which would require including include/prewin.h and
64 // include/postwin.h (to undo macros like Yield defined in windows.h) but which (unlike
65 // include/sal/main.h) are not part of the stable URE interface; so just ignore that
67 if (loplugin::DeclCheck(functionDecl
).Function("WinMain").GlobalNamespace())
72 report(DiagnosticsEngine::Warning
,
73 "function param %0 at definition site does not match function param at "
74 "declaration site, %1 vs %2",
75 thisParam
->getLocation())
76 << (i
+ 1) << thisParam
->getType() << canonicalParam
->getType()
77 << functionDecl
->getSourceRange();
78 report(DiagnosticsEngine::Note
, "declaration site here", canonicalParam
->getLocation())
79 << canonicalDecl
->getSourceRange();
83 if (!areTypesEqual(functionDecl
->getReturnType(), canonicalDecl
->getReturnType()))
85 report(DiagnosticsEngine::Warning
,
86 "function return type at definition site does not match function param at "
87 "declaration site, %0 vs %1",
88 compat::getBeginLoc(functionDecl
))
89 << functionDecl
->getReturnType() << canonicalDecl
->getReturnType()
90 << functionDecl
->getSourceRange();
91 report(DiagnosticsEngine::Note
, "declaration site here", compat::getBeginLoc(canonicalDecl
))
92 << canonicalDecl
->getSourceRange();
97 bool TypedefParam::VisitCXXMethodDecl(CXXMethodDecl
const* methodDecl
)
99 if (ignoreLocation(methodDecl
))
101 if (methodDecl
->isFunctionTemplateSpecialization())
103 // only warn on the declaration site
104 if (methodDecl
->getCanonicalDecl() != methodDecl
)
107 StringRef aFileName
= getFilenameOfLocation(
108 compiler
.getSourceManager().getSpellingLoc(compat::getBeginLoc(methodDecl
)));
109 // seems to be using typedefs as a form of documentation for method params
110 if (loplugin::hasPathnamePrefix(aFileName
, SRCDIR
"/sw/source/filter/ww8/ww8scan.hxx"))
112 // necessary to work around changes in popplers API
113 if (loplugin::hasPathnamePrefix(aFileName
, SRCDIR
114 "/sdext/source/pdfimport/xpdfwrapper/pdfioutdev_gpl.hxx"))
117 for (auto superMethodIt
= methodDecl
->begin_overridden_methods();
118 superMethodIt
!= methodDecl
->end_overridden_methods(); ++superMethodIt
)
120 const CXXMethodDecl
* superMethodDecl
= *superMethodIt
;
122 for (const ParmVarDecl
* superParmVarDecl
: superMethodDecl
->parameters())
124 const ParmVarDecl
* parmVarDecl
= methodDecl
->getParamDecl(i
);
125 if (!areTypesEqual(parmVarDecl
->getType(), superParmVarDecl
->getType()))
127 report(DiagnosticsEngine::Warning
,
128 "method param %0 does not match overridden method param "
130 parmVarDecl
->getLocation())
131 << (i
+ 1) << parmVarDecl
->getType() << superParmVarDecl
->getType()
132 << methodDecl
->getSourceRange();
133 report(DiagnosticsEngine::Note
, "super-class method here",
134 superParmVarDecl
->getLocation())
135 << superMethodDecl
->getSourceRange();
140 // it is quite normal to override a method and return a more specific pointer/reference type
141 auto returnType
= methodDecl
->getReturnType();
142 if (!returnType
->isPointerType() && !returnType
->isReferenceType())
143 if (!areTypesEqual(returnType
, superMethodDecl
->getReturnType()))
145 report(DiagnosticsEngine::Warning
,
146 "method return type does not match overridden method "
148 compat::getBeginLoc(methodDecl
))
149 << methodDecl
->getReturnType() << superMethodDecl
->getReturnType()
150 << methodDecl
->getSourceRange();
151 report(DiagnosticsEngine::Note
, "super-class method here",
152 compat::getBeginLoc(superMethodDecl
))
153 << superMethodDecl
->getSourceRange();
159 static bool areTypesEqual(QualType lhs
, QualType rhs
)
164 // ignore template stuff
165 if (lhs
->isDependentType() || rhs
->isDependentType())
167 // there are some places, e.g. chart2/source/controller/chartapiwrapper/WrappedSymbolProperties.cxx
168 // where just unwrapping these types does not work, so just ignore them
169 if (isa
<SubstTemplateTypeParmType
>(lhs
) || isa
<SubstTemplateTypeParmType
>(rhs
))
172 auto lhsType
= lhs
.getTypePtr();
173 auto rhsType
= rhs
.getTypePtr();
175 // this is the carve-out exception for the "typedef struct S {...} T" idiom we use in the UNO code
176 if (auto lhsPointer
= dyn_cast
<clang::PointerType
>(lhsType
))
178 if (auto rhsPointer
= dyn_cast
<clang::PointerType
>(rhsType
))
180 auto extractRecordType
= [](clang::QualType type
) {
181 auto recordType
= dyn_cast
<RecordType
>(type
);
184 auto elaboratedType
= dyn_cast
<ElaboratedType
>(type
);
186 return static_cast<const clang::RecordType
*>(nullptr);
187 return dyn_cast
<RecordType
>(elaboratedType
->desugar());
189 auto containsTypedefToRecord
= [](clang::QualType type
, RecordType
const* recordType
) {
190 TypedefType
const* typedefType
= dyn_cast
<TypedefType
>(type
);
193 auto tmp
= typedefType
->desugar();
194 if (auto elaboratedType
= dyn_cast
<ElaboratedType
>(tmp
))
195 tmp
= elaboratedType
->desugar();
196 return tmp
.getTypePtr() == recordType
;
198 if (auto recordType
= extractRecordType(lhsPointer
->getPointeeType()))
199 if (containsTypedefToRecord(rhsPointer
->getPointeeType(), recordType
))
201 if (auto recordType
= extractRecordType(rhsPointer
->getPointeeType()))
202 if (containsTypedefToRecord(lhsPointer
->getPointeeType(), recordType
))
207 if (auto lhsElaborated
= dyn_cast
<ElaboratedType
>(lhsType
))
209 return areTypesEqual(lhsElaborated
->getNamedType(), rhs
);
211 if (auto rhsElaborated
= dyn_cast
<ElaboratedType
>(rhsType
))
213 return areTypesEqual(lhs
, rhsElaborated
->getNamedType());
215 if (auto lhsTemplate
= dyn_cast
<TemplateSpecializationType
>(lhsType
))
217 return areTypesEqual(lhsTemplate
->desugar(), rhs
);
219 if (auto rhsTemplate
= dyn_cast
<TemplateSpecializationType
>(rhsType
))
221 return areTypesEqual(lhs
, rhsTemplate
->desugar());
224 if (auto lhsLValue
= dyn_cast
<LValueReferenceType
>(lhsType
))
226 auto rhsLValue
= dyn_cast
<LValueReferenceType
>(rhsType
);
229 return areTypesEqual(lhsLValue
->getPointeeType(), rhsLValue
->getPointeeType());
231 if (auto lhsRValue
= dyn_cast
<RValueReferenceType
>(lhsType
))
233 auto rhsRValue
= dyn_cast
<RValueReferenceType
>(rhsType
);
236 return areTypesEqual(lhsRValue
->getPointeeType(), rhsRValue
->getPointeeType());
238 if (auto lhsPointer
= dyn_cast
<clang::PointerType
>(lhsType
))
240 auto rhsPointer
= dyn_cast
<clang::PointerType
>(rhsType
);
243 return areTypesEqual(lhsPointer
->getPointeeType(), rhsPointer
->getPointeeType());
245 if (auto lhsTypedef
= dyn_cast
<TypedefType
>(lhsType
))
247 auto rhsTypedef
= dyn_cast
<TypedefType
>(rhsType
);
250 // comparing the underlying Decl's here doesn't work, they are not unique
251 if (lhsTypedef
->getDecl()->getName() != rhsTypedef
->getDecl()->getName())
253 #if defined __APPLE__
254 // My Clang trunk .../lib/clang/9.0.0/include/stddef.h has a
256 // typedef long unsigned int size_t;
258 // while /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/
259 // SDKs/MacOSX10.14.sdk/usr/include/sys/_types/_size_t.h has a
261 // typedef __darwin_size_t size_t;
263 // where __darwin_size_t is a typedef for long unsigned int, too, so that, depending on the
264 // order in which those two files get included, either of those two typedefs can act as a
265 // redeclaration of the other one. However, areTypesEqual would unhelpfully consider such
266 // different occurrences of size_t to be non-equal, so filter them out here. And, at least
267 // with my libcxx trunk .../include/c++/v1/cstddef, std::size_t is a using declaration that
268 // brings size_t from the global namespace into namespace std, so that the above checks for
269 // ElaboratedType sugar will already have unwrapped those to the size_t typedefs in the
270 // global namespace here.
271 if (loplugin::TypeCheck(lhsTypedef
).Typedef("size_t").GlobalNamespace()
272 && loplugin::TypeCheck(rhsTypedef
).Typedef("size_t").GlobalNamespace())
277 return areTypesEqual(lhsTypedef
->desugar(), rhsTypedef
->desugar());
279 if (auto lhsTemplate
= dyn_cast
<TemplateSpecializationType
>(lhsType
))
281 auto rhsTemplate
= dyn_cast
<TemplateSpecializationType
>(rhsType
);
284 return areTypesEqual(lhsTemplate
->desugar(), rhsTemplate
->desugar());
286 if (auto lhsMember
= dyn_cast
<MemberPointerType
>(lhsType
))
288 auto rhsMember
= dyn_cast
<MemberPointerType
>(rhsType
);
291 if (lhsMember
->getClass() != rhsMember
->getClass())
293 return areTypesEqual(lhsMember
->getPointeeType(), rhsMember
->getPointeeType());
295 if (auto lhsParen
= dyn_cast
<ParenType
>(lhsType
))
297 auto rhsParen
= dyn_cast
<ParenType
>(rhsType
);
300 return areTypesEqual(lhsParen
->getInnerType(), rhsParen
->getInnerType());
302 if (dyn_cast
<FunctionProtoType
>(lhsType
))
304 auto rhsFunction
= dyn_cast
<FunctionProtoType
>(rhsType
);
309 if (auto lhsDecayed
= dyn_cast
<DecayedType
>(lhsType
))
311 auto rhsDecayed
= dyn_cast
<DecayedType
>(rhsType
);
314 return areTypesEqual(lhsDecayed
->getAdjustedType(), rhsDecayed
->getAdjustedType());
316 if (auto lhsAttr
= dyn_cast
<AttributedType
>(lhsType
))
318 auto rhsAttr
= dyn_cast
<AttributedType
>(rhsType
);
321 return areTypesEqual(lhsAttr
->getModifiedType(), rhsAttr
->getModifiedType());
323 return lhsType
== rhsType
;
326 loplugin::Plugin::Registration
<TypedefParam
> typedefparam("typedefparam");
330 #endif // LO_CLANG_SHARED_PLUGINS
332 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */