bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / typedefparam.cxx
blob28e0a2d0946912db51b08964f9f63d975e48abab
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 #include <cassert>
11 #include <string>
12 #include <iostream>
13 #include <fstream>
14 #include <set>
16 #include "check.hxx"
17 #include "plugin.hxx"
19 /**
20 Check that parameters at the declaration site and the definition site are the same.
21 This can be important when refactoring, and can sometimes make a difference when compiling
22 for platforms with different pointer-sizes (e.g. 32 vs 64 bit)
24 namespace
26 class TypedefParam : public loplugin::FilteringRewritePlugin<TypedefParam>
28 public:
29 explicit TypedefParam(loplugin::InstantiationData const& data)
30 : FilteringRewritePlugin(data)
34 virtual void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
36 bool VisitFunctionDecl(FunctionDecl const*);
37 bool VisitCXXMethodDecl(CXXMethodDecl const*);
40 static bool areTypesEqual(QualType lhs, QualType rhs);
42 bool TypedefParam::VisitFunctionDecl(FunctionDecl const* functionDecl)
44 if (ignoreLocation(functionDecl))
45 return true;
46 if (functionDecl->isFunctionTemplateSpecialization())
47 return true;
48 auto canonicalDecl = functionDecl->getCanonicalDecl();
49 if (canonicalDecl == functionDecl)
50 return true;
52 for (unsigned i = 0; i < functionDecl->getNumParams(); ++i)
54 ParmVarDecl const* thisParam = functionDecl->getParamDecl(i);
55 ParmVarDecl const* canonicalParam = canonicalDecl->getParamDecl(i);
56 if (!areTypesEqual(thisParam->getType(), canonicalParam->getType()))
58 #if defined _WIN32
59 // SAL_IMPLEMENT_MAIN (include/sal/main.h) declares the third parameter of WinMain to be
60 // of type 'char *' rather than 'LPSTR', but using that typedef there would require
61 // including windows.h, which would require including include/prewin.h and
62 // include/postwin.h (to undo macros like Yield defined in windows.h) but which (unlike
63 // include/sal/main.h) are not part of the stable URE interface; so just ignore that
64 // here:
65 if (loplugin::DeclCheck(functionDecl).Function("WinMain").GlobalNamespace())
67 continue;
69 #endif
70 report(DiagnosticsEngine::Warning,
71 "function param %0 at definition site does not match function param at "
72 "declaration site, %1 vs %2",
73 thisParam->getLocation())
74 << (i + 1) << thisParam->getType() << canonicalParam->getType()
75 << functionDecl->getSourceRange();
76 report(DiagnosticsEngine::Note, "declaration site here", canonicalParam->getLocation())
77 << canonicalDecl->getSourceRange();
81 if (!areTypesEqual(functionDecl->getReturnType(), canonicalDecl->getReturnType()))
83 report(DiagnosticsEngine::Warning,
84 "function return type at definition site does not match function param at "
85 "declaration site, %0 vs %1",
86 compat::getBeginLoc(functionDecl))
87 << functionDecl->getReturnType() << canonicalDecl->getReturnType()
88 << functionDecl->getSourceRange();
89 report(DiagnosticsEngine::Note, "declaration site here", compat::getBeginLoc(canonicalDecl))
90 << canonicalDecl->getSourceRange();
92 return true;
95 bool TypedefParam::VisitCXXMethodDecl(CXXMethodDecl const* methodDecl)
97 if (ignoreLocation(methodDecl))
98 return true;
99 if (methodDecl->isFunctionTemplateSpecialization())
100 return true;
101 // only warn on the declaration site
102 if (methodDecl->getCanonicalDecl() != methodDecl)
103 return true;
105 StringRef aFileName = getFileNameOfSpellingLoc(
106 compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(methodDecl)));
107 // seems to be using typedefs as a form of documentation for method params
108 if (loplugin::hasPathnamePrefix(aFileName, SRCDIR "/sw/source/filter/ww8/ww8scan.hxx"))
109 return true;
110 // necessary to work around changes in popplers API
111 if (loplugin::hasPathnamePrefix(aFileName, SRCDIR
112 "/sdext/source/pdfimport/xpdfwrapper/pdfioutdev_gpl.hxx"))
113 return true;
115 for (auto superMethodIt = methodDecl->begin_overridden_methods();
116 superMethodIt != methodDecl->end_overridden_methods(); ++superMethodIt)
118 const CXXMethodDecl* superMethodDecl = *superMethodIt;
119 int i = 0;
120 for (const ParmVarDecl* superParmVarDecl : superMethodDecl->parameters())
122 const ParmVarDecl* parmVarDecl = methodDecl->getParamDecl(i);
123 if (!areTypesEqual(parmVarDecl->getType(), superParmVarDecl->getType()))
125 report(DiagnosticsEngine::Warning,
126 "method param %0 does not match overridden method param "
127 "%1 vs %2",
128 parmVarDecl->getLocation())
129 << (i + 1) << parmVarDecl->getType() << superParmVarDecl->getType()
130 << methodDecl->getSourceRange();
131 report(DiagnosticsEngine::Note, "super-class method here",
132 superParmVarDecl->getLocation())
133 << superMethodDecl->getSourceRange();
135 ++i;
138 // it is quite normal to override a method and return a more specific pointer/reference type
139 auto returnType = methodDecl->getReturnType();
140 if (!returnType->isPointerType() && !returnType->isReferenceType())
141 if (!areTypesEqual(returnType, superMethodDecl->getReturnType()))
143 report(DiagnosticsEngine::Warning,
144 "method return type does not match overridden method "
145 "%0 vs %1",
146 compat::getBeginLoc(methodDecl))
147 << methodDecl->getReturnType() << superMethodDecl->getReturnType()
148 << methodDecl->getSourceRange();
149 report(DiagnosticsEngine::Note, "super-class method here",
150 compat::getBeginLoc(superMethodDecl))
151 << superMethodDecl->getSourceRange();
154 return true;
157 static bool areTypesEqual(QualType lhs, QualType rhs)
159 if (lhs == rhs)
160 return true;
162 // ignore template stuff
163 if (lhs->isDependentType() || rhs->isDependentType())
164 return true;
165 // there are some places, e.g. chart2/source/controller/chartapiwrapper/WrappedSymbolProperties.cxx
166 // where just unwrapping these types does not work, so just ignore them
167 if (isa<SubstTemplateTypeParmType>(lhs) || isa<SubstTemplateTypeParmType>(rhs))
168 return true;
170 auto lhsType = lhs.getTypePtr();
171 auto rhsType = rhs.getTypePtr();
173 // this is the carve-out exception for the "typedef struct S {...} T" idiom we use in the UNO code
174 if (auto lhsPointer = dyn_cast<clang::PointerType>(lhsType))
176 if (auto rhsPointer = dyn_cast<clang::PointerType>(rhsType))
178 auto extractRecordType = [](clang::QualType type) {
179 auto recordType = dyn_cast<RecordType>(type);
180 if (recordType)
181 return recordType;
182 auto elaboratedType = dyn_cast<ElaboratedType>(type);
183 if (!elaboratedType)
184 return static_cast<const clang::RecordType*>(nullptr);
185 return dyn_cast<RecordType>(elaboratedType->desugar());
187 auto containsTypedefToRecord = [](clang::QualType type, RecordType const* recordType) {
188 TypedefType const* typedefType = dyn_cast<TypedefType>(type);
189 if (!typedefType)
190 return false;
191 auto tmp = typedefType->desugar();
192 if (auto elaboratedType = dyn_cast<ElaboratedType>(tmp))
193 tmp = elaboratedType->desugar();
194 return tmp.getTypePtr() == recordType;
196 if (auto recordType = extractRecordType(lhsPointer->getPointeeType()))
197 if (containsTypedefToRecord(rhsPointer->getPointeeType(), recordType))
198 return true;
199 if (auto recordType = extractRecordType(rhsPointer->getPointeeType()))
200 if (containsTypedefToRecord(lhsPointer->getPointeeType(), recordType))
201 return true;
205 if (auto lhsElaborated = dyn_cast<ElaboratedType>(lhsType))
207 return areTypesEqual(lhsElaborated->getNamedType(), rhs);
209 if (auto rhsElaborated = dyn_cast<ElaboratedType>(rhsType))
211 return areTypesEqual(lhs, rhsElaborated->getNamedType());
213 if (auto lhsTemplate = dyn_cast<TemplateSpecializationType>(lhsType))
215 return areTypesEqual(lhsTemplate->desugar(), rhs);
217 if (auto rhsTemplate = dyn_cast<TemplateSpecializationType>(rhsType))
219 return areTypesEqual(lhs, rhsTemplate->desugar());
222 if (auto lhsLValue = dyn_cast<LValueReferenceType>(lhsType))
224 auto rhsLValue = dyn_cast<LValueReferenceType>(rhsType);
225 if (!rhsLValue)
226 return false;
227 return areTypesEqual(lhsLValue->getPointeeType(), rhsLValue->getPointeeType());
229 if (auto lhsRValue = dyn_cast<RValueReferenceType>(lhsType))
231 auto rhsRValue = dyn_cast<RValueReferenceType>(rhsType);
232 if (!rhsRValue)
233 return false;
234 return areTypesEqual(lhsRValue->getPointeeType(), rhsRValue->getPointeeType());
236 if (auto lhsPointer = dyn_cast<clang::PointerType>(lhsType))
238 auto rhsPointer = dyn_cast<clang::PointerType>(rhsType);
239 if (!rhsPointer)
240 return false;
241 return areTypesEqual(lhsPointer->getPointeeType(), rhsPointer->getPointeeType());
243 if (auto lhsTypedef = dyn_cast<TypedefType>(lhsType))
245 auto rhsTypedef = dyn_cast<TypedefType>(rhsType);
246 if (!rhsTypedef)
247 return false;
248 // comparing the underlying Decl's here doesn't work, they are not unique
249 if (lhsTypedef->getDecl()->getName() != rhsTypedef->getDecl()->getName())
250 return false;
251 return areTypesEqual(lhsTypedef->desugar(), rhsTypedef->desugar());
253 if (auto lhsTemplate = dyn_cast<TemplateSpecializationType>(lhsType))
255 auto rhsTemplate = dyn_cast<TemplateSpecializationType>(rhsType);
256 if (!rhsTemplate)
257 return false;
258 return areTypesEqual(lhsTemplate->desugar(), rhsTemplate->desugar());
260 if (auto lhsMember = dyn_cast<MemberPointerType>(lhsType))
262 auto rhsMember = dyn_cast<MemberPointerType>(rhsType);
263 if (!rhsMember)
264 return false;
265 if (lhsMember->getClass() != rhsMember->getClass())
266 return true;
267 return areTypesEqual(lhsMember->getPointeeType(), rhsMember->getPointeeType());
269 if (auto lhsParen = dyn_cast<ParenType>(lhsType))
271 auto rhsParen = dyn_cast<ParenType>(rhsType);
272 if (!rhsParen)
273 return false;
274 return areTypesEqual(lhsParen->getInnerType(), rhsParen->getInnerType());
276 if (dyn_cast<FunctionProtoType>(lhsType))
278 auto rhsFunction = dyn_cast<FunctionProtoType>(rhsType);
279 if (!rhsFunction)
280 return false;
281 return true; // TODO
283 if (auto lhsDecayed = dyn_cast<DecayedType>(lhsType))
285 auto rhsDecayed = dyn_cast<DecayedType>(rhsType);
286 if (!rhsDecayed)
287 return false;
288 return areTypesEqual(lhsDecayed->getAdjustedType(), rhsDecayed->getAdjustedType());
290 if (auto lhsAttr = dyn_cast<AttributedType>(lhsType))
292 auto rhsAttr = dyn_cast<AttributedType>(rhsType);
293 if (!rhsAttr)
294 return false;
295 return areTypesEqual(lhsAttr->getModifiedType(), rhsAttr->getModifiedType());
297 return lhsType == rhsType;
300 loplugin::Plugin::Registration<TypedefParam> X("typedefparam", true);
303 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */