summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/typedefparam.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang/typedefparam.cxx')
-rw-r--r--compilerplugins/clang/typedefparam.cxx334
1 files changed, 334 insertions, 0 deletions
diff --git a/compilerplugins/clang/typedefparam.cxx b/compilerplugins/clang/typedefparam.cxx
new file mode 100644
index 000000000..b7e57b2a1
--- /dev/null
+++ b/compilerplugins/clang/typedefparam.cxx
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef LO_CLANG_SHARED_PLUGINS
+
+#include <cassert>
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <set>
+
+#include "config_clang.h"
+
+#include "check.hxx"
+#include "plugin.hxx"
+
+/**
+ Check that parameters at the declaration site and the definition site are the same.
+ This can be important when refactoring, and can sometimes make a difference when compiling
+ for platforms with different pointer-sizes (e.g. 32 vs 64 bit)
+ */
+namespace
+{
+class TypedefParam : public loplugin::FilteringRewritePlugin<TypedefParam>
+{
+public:
+ explicit TypedefParam(loplugin::InstantiationData const& data)
+ : FilteringRewritePlugin(data)
+ {
+ }
+
+ void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
+
+ bool VisitFunctionDecl(FunctionDecl const*);
+ bool VisitCXXMethodDecl(CXXMethodDecl const*);
+};
+
+static bool areTypesEqual(QualType lhs, QualType rhs);
+
+bool TypedefParam::VisitFunctionDecl(FunctionDecl const* functionDecl)
+{
+ if (ignoreLocation(functionDecl))
+ return true;
+ if (functionDecl->isFunctionTemplateSpecialization())
+ return true;
+ auto canonicalDecl = functionDecl->getCanonicalDecl();
+ if (canonicalDecl == functionDecl)
+ return true;
+
+ for (unsigned i = 0; i < functionDecl->getNumParams(); ++i)
+ {
+ ParmVarDecl const* thisParam = functionDecl->getParamDecl(i);
+ ParmVarDecl const* canonicalParam = canonicalDecl->getParamDecl(i);
+ if (!areTypesEqual(thisParam->getType(), canonicalParam->getType()))
+ {
+#if defined _WIN32
+ // SAL_IMPLEMENT_MAIN (include/sal/main.h) declares the third parameter of WinMain to be
+ // of type 'char *' rather than 'LPSTR', but using that typedef there would require
+ // including windows.h, which would require including include/prewin.h and
+ // include/postwin.h (to undo macros like Yield defined in windows.h) but which (unlike
+ // include/sal/main.h) are not part of the stable URE interface; so just ignore that
+ // here:
+ if (loplugin::DeclCheck(functionDecl).Function("WinMain").GlobalNamespace())
+ {
+ continue;
+ }
+#endif
+ report(DiagnosticsEngine::Warning,
+ "function param %0 at definition site does not match function param at "
+ "declaration site, %1 vs %2",
+ thisParam->getLocation())
+ << (i + 1) << thisParam->getType() << canonicalParam->getType()
+ << functionDecl->getSourceRange();
+ report(DiagnosticsEngine::Note, "declaration site here", canonicalParam->getLocation())
+ << canonicalDecl->getSourceRange();
+ }
+ }
+
+ if (!areTypesEqual(functionDecl->getReturnType(), canonicalDecl->getReturnType()))
+ {
+ report(DiagnosticsEngine::Warning,
+ "function return type at definition site does not match function param at "
+ "declaration site, %0 vs %1",
+ functionDecl->getBeginLoc())
+ << functionDecl->getReturnType() << canonicalDecl->getReturnType()
+ << functionDecl->getSourceRange();
+ report(DiagnosticsEngine::Note, "declaration site here", canonicalDecl->getBeginLoc())
+ << canonicalDecl->getSourceRange();
+ }
+ return true;
+}
+
+bool TypedefParam::VisitCXXMethodDecl(CXXMethodDecl const* methodDecl)
+{
+ if (ignoreLocation(methodDecl))
+ return true;
+ if (methodDecl->isFunctionTemplateSpecialization())
+ return true;
+ // only warn on the declaration site
+ if (methodDecl->getCanonicalDecl() != methodDecl)
+ return true;
+
+ StringRef aFileName = getFilenameOfLocation(
+ compiler.getSourceManager().getSpellingLoc(methodDecl->getBeginLoc()));
+ // seems to be using typedefs as a form of documentation for method params
+ if (loplugin::hasPathnamePrefix(aFileName, SRCDIR "/sw/source/filter/ww8/ww8scan.hxx"))
+ return true;
+ // necessary to work around changes in popplers API
+ if (loplugin::hasPathnamePrefix(aFileName, SRCDIR
+ "/sdext/source/pdfimport/xpdfwrapper/pdfioutdev_gpl.hxx"))
+ return true;
+
+ for (auto superMethodIt = methodDecl->begin_overridden_methods();
+ superMethodIt != methodDecl->end_overridden_methods(); ++superMethodIt)
+ {
+ const CXXMethodDecl* superMethodDecl = *superMethodIt;
+ int i = 0;
+ for (const ParmVarDecl* superParmVarDecl : superMethodDecl->parameters())
+ {
+ const ParmVarDecl* parmVarDecl = methodDecl->getParamDecl(i);
+ if (!areTypesEqual(parmVarDecl->getType(), superParmVarDecl->getType()))
+ {
+ report(DiagnosticsEngine::Warning,
+ "method param %0 does not match overridden method param "
+ "%1 vs %2",
+ parmVarDecl->getLocation())
+ << (i + 1) << parmVarDecl->getType() << superParmVarDecl->getType()
+ << methodDecl->getSourceRange();
+ report(DiagnosticsEngine::Note, "super-class method here",
+ superParmVarDecl->getLocation())
+ << superMethodDecl->getSourceRange();
+ }
+ ++i;
+ }
+
+ // it is quite normal to override a method and return a more specific pointer/reference type
+ auto returnType = methodDecl->getReturnType();
+ if (!returnType->isPointerType() && !returnType->isReferenceType())
+ if (!areTypesEqual(returnType, superMethodDecl->getReturnType()))
+ {
+ report(DiagnosticsEngine::Warning,
+ "method return type does not match overridden method "
+ "%0 vs %1",
+ methodDecl->getBeginLoc())
+ << methodDecl->getReturnType() << superMethodDecl->getReturnType()
+ << methodDecl->getSourceRange();
+ report(DiagnosticsEngine::Note, "super-class method here",
+ superMethodDecl->getBeginLoc())
+ << superMethodDecl->getSourceRange();
+ }
+ }
+ return true;
+}
+
+static bool areTypesEqual(QualType lhs, QualType rhs)
+{
+ if (lhs == rhs)
+ return true;
+
+ // ignore template stuff
+ if (lhs->isDependentType() || rhs->isDependentType())
+ return true;
+ // there are some places, e.g. chart2/source/controller/chartapiwrapper/WrappedSymbolProperties.cxx
+ // where just unwrapping these types does not work, so just ignore them
+ if (isa<SubstTemplateTypeParmType>(lhs) || isa<SubstTemplateTypeParmType>(rhs))
+ return true;
+
+ auto lhsType = lhs.getTypePtr();
+ auto rhsType = rhs.getTypePtr();
+
+ // this is the carve-out exception for the "typedef struct S {...} T" idiom we use in the UNO code
+ if (auto lhsPointer = dyn_cast<clang::PointerType>(lhsType))
+ {
+ if (auto rhsPointer = dyn_cast<clang::PointerType>(rhsType))
+ {
+ auto extractRecordType = [](clang::QualType type) {
+ auto recordType = dyn_cast<RecordType>(type);
+ if (recordType)
+ return recordType;
+ auto elaboratedType = dyn_cast<ElaboratedType>(type);
+ if (!elaboratedType)
+ return static_cast<const clang::RecordType*>(nullptr);
+ return dyn_cast<RecordType>(elaboratedType->desugar());
+ };
+ auto containsTypedefToRecord = [](clang::QualType type, RecordType const* recordType) {
+ TypedefType const* typedefType = dyn_cast<TypedefType>(type);
+ if (!typedefType)
+ return false;
+ auto tmp = typedefType->desugar();
+ if (auto elaboratedType = dyn_cast<ElaboratedType>(tmp))
+ tmp = elaboratedType->desugar();
+ return tmp.getTypePtr() == recordType;
+ };
+ if (auto recordType = extractRecordType(lhsPointer->getPointeeType()))
+ if (containsTypedefToRecord(rhsPointer->getPointeeType(), recordType))
+ return true;
+ if (auto recordType = extractRecordType(rhsPointer->getPointeeType()))
+ if (containsTypedefToRecord(lhsPointer->getPointeeType(), recordType))
+ return true;
+ }
+ }
+
+ if (auto lhsElaborated = dyn_cast<ElaboratedType>(lhsType))
+ {
+ return areTypesEqual(lhsElaborated->getNamedType(), rhs);
+ }
+ if (auto rhsElaborated = dyn_cast<ElaboratedType>(rhsType))
+ {
+ return areTypesEqual(lhs, rhsElaborated->getNamedType());
+ }
+ if (auto lhsTemplate = dyn_cast<TemplateSpecializationType>(lhsType))
+ {
+ return areTypesEqual(lhsTemplate->desugar(), rhs);
+ }
+ if (auto rhsTemplate = dyn_cast<TemplateSpecializationType>(rhsType))
+ {
+ return areTypesEqual(lhs, rhsTemplate->desugar());
+ }
+
+ if (auto lhsLValue = dyn_cast<LValueReferenceType>(lhsType))
+ {
+ auto rhsLValue = dyn_cast<LValueReferenceType>(rhsType);
+ if (!rhsLValue)
+ return false;
+ return areTypesEqual(lhsLValue->getPointeeType(), rhsLValue->getPointeeType());
+ }
+ if (auto lhsRValue = dyn_cast<RValueReferenceType>(lhsType))
+ {
+ auto rhsRValue = dyn_cast<RValueReferenceType>(rhsType);
+ if (!rhsRValue)
+ return false;
+ return areTypesEqual(lhsRValue->getPointeeType(), rhsRValue->getPointeeType());
+ }
+ if (auto lhsPointer = dyn_cast<clang::PointerType>(lhsType))
+ {
+ auto rhsPointer = dyn_cast<clang::PointerType>(rhsType);
+ if (!rhsPointer)
+ return false;
+ return areTypesEqual(lhsPointer->getPointeeType(), rhsPointer->getPointeeType());
+ }
+ if (auto lhsTypedef = dyn_cast<TypedefType>(lhsType))
+ {
+ auto rhsTypedef = dyn_cast<TypedefType>(rhsType);
+ if (!rhsTypedef)
+ return false;
+ // comparing the underlying Decl's here doesn't work, they are not unique
+ if (lhsTypedef->getDecl()->getName() != rhsTypedef->getDecl()->getName())
+ return false;
+#if defined __APPLE__
+ // My Clang trunk .../lib/clang/9.0.0/include/stddef.h has a
+ //
+ // typedef long unsigned int size_t;
+ //
+ // while /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/
+ // SDKs/MacOSX10.14.sdk/usr/include/sys/_types/_size_t.h has a
+ //
+ // typedef __darwin_size_t size_t;
+ //
+ // where __darwin_size_t is a typedef for long unsigned int, too, so that, depending on the
+ // order in which those two files get included, either of those two typedefs can act as a
+ // redeclaration of the other one. However, areTypesEqual would unhelpfully consider such
+ // different occurrences of size_t to be non-equal, so filter them out here. And, at least
+ // with my libcxx trunk .../include/c++/v1/cstddef, std::size_t is a using declaration that
+ // brings size_t from the global namespace into namespace std, so that the above checks for
+ // ElaboratedType sugar will already have unwrapped those to the size_t typedefs in the
+ // global namespace here.
+ if (loplugin::TypeCheck(lhsTypedef).Typedef("size_t").GlobalNamespace()
+ && loplugin::TypeCheck(rhsTypedef).Typedef("size_t").GlobalNamespace())
+ {
+ return true;
+ }
+#endif
+ return areTypesEqual(lhsTypedef->desugar(), rhsTypedef->desugar());
+ }
+ if (auto lhsTemplate = dyn_cast<TemplateSpecializationType>(lhsType))
+ {
+ auto rhsTemplate = dyn_cast<TemplateSpecializationType>(rhsType);
+ if (!rhsTemplate)
+ return false;
+ return areTypesEqual(lhsTemplate->desugar(), rhsTemplate->desugar());
+ }
+ if (auto lhsMember = dyn_cast<MemberPointerType>(lhsType))
+ {
+ auto rhsMember = dyn_cast<MemberPointerType>(rhsType);
+ if (!rhsMember)
+ return false;
+ if (lhsMember->getClass() != rhsMember->getClass())
+ return true;
+ return areTypesEqual(lhsMember->getPointeeType(), rhsMember->getPointeeType());
+ }
+ if (auto lhsParen = dyn_cast<ParenType>(lhsType))
+ {
+ auto rhsParen = dyn_cast<ParenType>(rhsType);
+ if (!rhsParen)
+ return false;
+ return areTypesEqual(lhsParen->getInnerType(), rhsParen->getInnerType());
+ }
+ if (dyn_cast<FunctionProtoType>(lhsType))
+ {
+ auto rhsFunction = dyn_cast<FunctionProtoType>(rhsType);
+ if (!rhsFunction)
+ return false;
+ return true; // TODO
+ }
+ if (auto lhsDecayed = dyn_cast<DecayedType>(lhsType))
+ {
+ auto rhsDecayed = dyn_cast<DecayedType>(rhsType);
+ if (!rhsDecayed)
+ return false;
+ return areTypesEqual(lhsDecayed->getAdjustedType(), rhsDecayed->getAdjustedType());
+ }
+ if (auto lhsAttr = dyn_cast<AttributedType>(lhsType))
+ {
+ auto rhsAttr = dyn_cast<AttributedType>(rhsType);
+ if (!rhsAttr)
+ return false;
+ return areTypesEqual(lhsAttr->getModifiedType(), rhsAttr->getModifiedType());
+ }
+ return lhsType == rhsType;
+}
+
+loplugin::Plugin::Registration<TypedefParam> typedefparam("typedefparam");
+
+} // namespace
+
+#endif // LO_CLANG_SHARED_PLUGINS
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */