d1a2b80b9d
...as discussed in the mail thread starting at <https://lists.freedesktop.org/archives/libreoffice/2020-November/086234.html> "Bump --enable-compiler-plugins Clang baseline?" (and now picked up again at <https://lists.freedesktop.org/archives/libreoffice/2022-February/088459.html> "Re: Bump --enable-compiler-plugins Clang baseline?"), and clean up compilerplugins/clang/ accordingly Change-Id: I5e81c6fdcc363aeefd6227606225b526fdf7ac16 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129989 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
334 lines
14 KiB
C++
334 lines
14 KiB
C++
/* -*- 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: */
|