summaryrefslogtreecommitdiff
path: root/compilerplugins/clang
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang')
-rw-r--r--compilerplugins/clang/fakebool.cxx (renamed from compilerplugins/clang/salbool.cxx)720
-rw-r--r--compilerplugins/clang/test/fakebool.cxx (renamed from compilerplugins/clang/test/salbool.cxx)6
2 files changed, 477 insertions, 249 deletions
diff --git a/compilerplugins/clang/salbool.cxx b/compilerplugins/clang/fakebool.cxx
index fdadfc6b795a..1dbb535ceef9 100644
--- a/compilerplugins/clang/salbool.cxx
+++ b/compilerplugins/clang/fakebool.cxx
@@ -10,27 +10,167 @@
#include <algorithm>
#include <cassert>
#include <limits>
-#include <set>
+#include <map>
#include <string>
#include "clang/AST/Attr.h"
#include "check.hxx"
#include "compat.hxx"
+#include "functionaddress.hxx"
#include "plugin.hxx"
namespace {
-bool isSalBool(QualType type) {
+// BEGIN code copied from LLVM's clang/lib/Sema/Sema.cpp
+
+typedef llvm::DenseMap<const CXXRecordDecl*, bool> RecordCompleteMap;
+
+/// Returns true, if all methods and nested classes of the given
+/// CXXRecordDecl are defined in this translation unit.
+///
+/// Should only be called from ActOnEndOfTranslationUnit so that all
+/// definitions are actually read.
+static bool MethodsAndNestedClassesComplete(const CXXRecordDecl *RD,
+ RecordCompleteMap &MNCComplete) {
+ RecordCompleteMap::iterator Cache = MNCComplete.find(RD);
+ if (Cache != MNCComplete.end())
+ return Cache->second;
+ if (!RD->isCompleteDefinition())
+ return false;
+ bool Complete = true;
+ for (DeclContext::decl_iterator I = RD->decls_begin(),
+ E = RD->decls_end();
+ I != E && Complete; ++I) {
+ if (const CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(*I))
+ Complete = M->isDefined() || M->isDefaulted() ||
+ (M->isPure() && !isa<CXXDestructorDecl>(M));
+ else if (const FunctionTemplateDecl *F = dyn_cast<FunctionTemplateDecl>(*I))
+ // If the template function is marked as late template parsed at this
+ // point, it has not been instantiated and therefore we have not
+ // performed semantic analysis on it yet, so we cannot know if the type
+ // can be considered complete.
+ Complete = !F->getTemplatedDecl()->isLateTemplateParsed() &&
+ F->getTemplatedDecl()->isDefined();
+ else if (const CXXRecordDecl *R = dyn_cast<CXXRecordDecl>(*I)) {
+ if (R->isInjectedClassName())
+ continue;
+ if (R->hasDefinition())
+ Complete = MethodsAndNestedClassesComplete(R->getDefinition(),
+ MNCComplete);
+ else
+ Complete = false;
+ }
+ }
+ MNCComplete[RD] = Complete;
+ return Complete;
+}
+
+/// Returns true, if the given CXXRecordDecl is fully defined in this
+/// translation unit, i.e. all methods are defined or pure virtual and all
+/// friends, friend functions and nested classes are fully defined in this
+/// translation unit.
+///
+/// Should only be called from ActOnEndOfTranslationUnit so that all
+/// definitions are actually read.
+static bool IsRecordFullyDefined(const CXXRecordDecl *RD,
+ RecordCompleteMap &RecordsComplete,
+ RecordCompleteMap &MNCComplete) {
+ RecordCompleteMap::iterator Cache = RecordsComplete.find(RD);
+ if (Cache != RecordsComplete.end())
+ return Cache->second;
+ bool Complete = MethodsAndNestedClassesComplete(RD, MNCComplete);
+ for (CXXRecordDecl::friend_iterator I = RD->friend_begin(),
+ E = RD->friend_end();
+ I != E && Complete; ++I) {
+ // Check if friend classes and methods are complete.
+ if (TypeSourceInfo *TSI = (*I)->getFriendType()) {
+ // Friend classes are available as the TypeSourceInfo of the FriendDecl.
+ if (CXXRecordDecl *FriendD = TSI->getType()->getAsCXXRecordDecl())
+ Complete = MethodsAndNestedClassesComplete(FriendD, MNCComplete);
+ else
+ Complete = false;
+ } else {
+ // Friend functions are available through the NamedDecl of FriendDecl.
+ if (const FunctionDecl *FD =
+ dyn_cast<FunctionDecl>((*I)->getFriendDecl()))
+ Complete = FD->isDefined();
+ else
+ // This is a template friend, give up.
+ Complete = false;
+ }
+ }
+ RecordsComplete[RD] = Complete;
+ return Complete;
+}
+
+RecordCompleteMap RecordsComplete;
+RecordCompleteMap MNCComplete;
+
+// END code copied from LLVM's clang/lib/Sema/Sema.cpp
+
+// Is all code that could see `decl` defined in this TU?
+bool isAllRelevantCodeDefined(NamedDecl const * decl) {
+ switch (decl->getAccess()) {
+ case AS_protected:
+ if (!cast<CXXRecordDecl>(decl->getDeclContext())->hasAttr<FinalAttr>()) {
+ break;
+ }
+ LLVM_FALLTHROUGH;
+ case AS_private:
+ if (IsRecordFullyDefined(
+ cast<CXXRecordDecl>(decl->getDeclContext()), RecordsComplete, MNCComplete))
+ {
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return !decl->isExternallyVisible();
+}
+
+enum FakeBoolKind {
+ FBK_No,
+ FBK_BOOL, FBK_First = FBK_BOOL,
+ FBK_Boolean, FBK_FT_Bool, FBK_FcBool, FBK_GLboolean, FBK_NPBool, FBK_TW_BOOL, FBK_UBool,
+ FBK_boolean, FBK_dbus_bool_t, FBK_gboolean, FBK_hb_boot_t, FBK_jboolean, FBK_my_bool,
+ FBK_sal_Bool,
+ FBK_End };
+ // matches loplugin::TypeCheck::AnyBoolean (compilerplugins/clang/check.hxx)
+
+StringRef getName(FakeBoolKind k) {
+ static constexpr llvm::StringLiteral names[] = {
+ "BOOL", "Boolean", "FT_Bool", "FcBool", "GLboolean", "NPBool", "TW_BOOL", "UBool",
+ "boolean", "dbus_bool_t", "gboolean", "hb_boot_t", "jboolean", "my_bool", "sal_Bool"};
+ assert(k >= FBK_First && k < FBK_End);
+ return names[k - FBK_First];
+}
+
+FakeBoolKind isFakeBool(QualType type) {
TypedefType const * t = type->getAs<TypedefType>();
- return t != nullptr && t->getDecl()->getName() == "sal_Bool";
+ if (t != nullptr) {
+ auto const name = t->getDecl()->getName();
+ for (int i = FBK_First; i != FBK_End; ++i) {
+ auto const k = FakeBoolKind(i);
+ if (name == getName(k)) {
+ return k;
+ }
+ }
+ }
+ return FBK_No;
}
-bool isSalBoolArray(QualType type) {
+FakeBoolKind isFakeBoolArray(QualType type) {
auto t = type->getAsArrayTypeUnsafe();
- return t != nullptr
- && (isSalBool(t->getElementType())
- || isSalBoolArray(t->getElementType()));
+ if (t == nullptr) {
+ return FBK_No;
+ }
+ auto const k = isFakeBool(t->getElementType());
+ if (k != FBK_No) {
+ return k;
+ }
+ return isFakeBoolArray(t->getElementType());
}
// It appears that, given a function declaration, there is no way to determine
@@ -78,27 +218,27 @@ BoolOverloadKind isBoolOverloadOf(
if (!mustBeDeleted || f->isDeleted()) {
unsigned n = decl->getNumParams();
if (f->getNumParams() == n) {
- bool hasSB = false;
+ bool hasFB = false;
for (unsigned i = 0; i != n; ++i) {
QualType t1 { decl->getParamDecl(i)->getType() };
- bool isSB = isSalBool(t1);
- bool isSBRef = !isSB && t1->isReferenceType()
- && isSalBool(t1.getNonReferenceType());
+ bool isFB = isFakeBool(t1) != FBK_No;
+ bool isFBRef = !isFB && t1->isReferenceType()
+ && isFakeBool(t1.getNonReferenceType()) != FBK_No;
QualType t2 { f->getParamDecl(i)->getType() };
- if (!(isSB
+ if (!(isFB
? t2->isBooleanType()
- : isSBRef
+ : isFBRef
? (t2->isReferenceType()
&& t2.getNonReferenceType()->isBooleanType())
: t2.getCanonicalType() == t1.getCanonicalType()))
{
return BoolOverloadKind::CheckNext;
}
- hasSB |= isSB || isSBRef;
+ hasFB |= isFB || isFBRef;
}
- return hasSB ? BoolOverloadKind::Yes : BoolOverloadKind::No;
+ return hasFB ? BoolOverloadKind::Yes : BoolOverloadKind::No;
// cheaply protect against the case where decl would have no
- // sal_Bool parameters at all and would match itself
+ // fake bool parameters at all and would match itself
}
}
return BoolOverloadKind::CheckNext;
@@ -142,12 +282,12 @@ bool hasBoolOverload(FunctionDecl const * decl, bool mustBeDeleted) {
return false;
}
-class SalBool:
- public loplugin::FilteringRewritePlugin<SalBool>
+class FakeBool:
+ public loplugin::FunctionAddress<loplugin::FilteringRewritePlugin<FakeBool>>
{
public:
- explicit SalBool(loplugin::InstantiationData const & data):
- FilteringRewritePlugin(data) {}
+ explicit FakeBool(loplugin::InstantiationData const & data):
+ FunctionAddress(data) {}
virtual void run() override;
@@ -190,16 +330,20 @@ private:
bool isInSpecialMainFile(SourceLocation spellingLocation) const;
- bool rewrite(SourceLocation location);
+ bool rewrite(SourceLocation location, FakeBoolKind kind);
- std::set<VarDecl const *> varDecls_;
+ std::map<VarDecl const *, FakeBoolKind> varDecls_;
+ std::map<FieldDecl const *, FakeBoolKind> fieldDecls_;
+ std::map<ParmVarDecl const *, FakeBoolKind> parmVarDecls_;
+ std::map<FunctionDecl const *, FakeBoolKind> functionDecls_;
unsigned int externCContexts_ = 0;
};
-void SalBool::run() {
+void FakeBool::run() {
if (compiler.getLangOpts().CPlusPlus) {
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
- for (auto decl: varDecls_) {
+ for (auto const dcl: varDecls_) {
+ auto const decl = dcl.first; auto const fbk = dcl.second;
SourceLocation loc { compat::getBeginLoc(decl) };
TypeSourceInfo * tsi = decl->getTypeSourceInfo();
if (tsi != nullptr) {
@@ -221,7 +365,95 @@ void SalBool::run() {
std::string s {
compiler.getSourceManager().getCharacterData(l),
n };
- if (s == "sal_Bool") {
+ if (s == getName(fbk)) {
+ loc = l;
+ break;
+ }
+ if (l == end) {
+ break;
+ }
+ l = l.getLocWithOffset(std::max<unsigned>(n, 1));
+ }
+ }
+ }
+ if (!rewrite(loc, fbk)) {
+ report(
+ DiagnosticsEngine::Warning,
+ "VarDecl, use \"bool\" instead of %0", loc)
+ << decl->getType().getLocalUnqualifiedType()
+ << decl->getSourceRange();
+ }
+ }
+ for (auto const dcl: fieldDecls_) {
+ auto const decl = dcl.first; auto const fbk = dcl.second;
+ SourceLocation loc { compat::getBeginLoc(decl) };
+ TypeSourceInfo * tsi = decl->getTypeSourceInfo();
+ if (tsi != nullptr) {
+ SourceLocation l {
+ compiler.getSourceManager().getExpansionLoc(
+ tsi->getTypeLoc().getBeginLoc()) };
+ SourceLocation end {
+ compiler.getSourceManager().getExpansionLoc(
+ tsi->getTypeLoc().getEndLoc()) };
+ assert(l.isFileID() && end.isFileID());
+ if (l == end
+ || compiler.getSourceManager().isBeforeInTranslationUnit(
+ l, end))
+ {
+ for (;;) {
+ unsigned n = Lexer::MeasureTokenLength(
+ l, compiler.getSourceManager(),
+ compiler.getLangOpts());
+ std::string s {
+ compiler.getSourceManager().getCharacterData(l),
+ n };
+ if (s == getName(fbk)) {
+ loc = l;
+ break;
+ }
+ if (l == end) {
+ break;
+ }
+ l = l.getLocWithOffset(std::max<unsigned>(n, 1));
+ }
+ }
+ }
+ if (!rewrite(loc, fbk)) {
+ report(
+ DiagnosticsEngine::Warning,
+ "FieldDecl, use \"bool\" instead of %0", loc)
+ << decl->getType().getLocalUnqualifiedType() << decl->getSourceRange();
+ }
+ }
+ auto const ignoredFns = getFunctionsWithAddressTaken();
+ for (auto const dcl: parmVarDecls_) {
+ auto const decl = dcl.first; auto const fbk = dcl.second;
+ FunctionDecl const * f = cast<FunctionDecl>(decl->getDeclContext())->getCanonicalDecl();
+ if (ignoredFns.find(f) != ignoredFns.end()) {
+ continue;
+ }
+ SourceLocation loc { compat::getBeginLoc(decl) };
+ TypeSourceInfo * tsi = decl->getTypeSourceInfo();
+ if (tsi != nullptr) {
+ SourceLocation l {
+ compiler.getSourceManager().getExpansionLoc(
+ tsi->getTypeLoc().getBeginLoc()) };
+ SourceLocation end {
+ compiler.getSourceManager().getExpansionLoc(
+ tsi->getTypeLoc().getEndLoc()) };
+ assert(l.isFileID() && end.isFileID());
+ if (l == end
+ || (compiler.getSourceManager()
+ .isBeforeInTranslationUnit(l, end)))
+ {
+ for (;;) {
+ unsigned n = Lexer::MeasureTokenLength(
+ l, compiler.getSourceManager(),
+ compiler.getLangOpts());
+ std::string s {
+ compiler.getSourceManager().getCharacterData(l),
+ n };
+ if (s == getName(fbk)) {
loc = l;
break;
}
@@ -232,31 +464,114 @@ void SalBool::run() {
}
}
}
- if (!rewrite(loc)) {
+ // Only rewrite declarations in include files if a
+ // definition is also seen, to avoid compilation of a
+ // definition (in a main file only processed later) to fail
+ // with a "mismatch" error before the rewriter had a chance
+ // to act upon the definition (but use the heuristic of
+ // assuming pure virtual functions do not have definitions);
+ // also, do not automatically rewrite functions that could
+ // implicitly override depend base functions (and thus stop
+ // doing so after the rewrite; note that this is less
+ // dangerous for return types than for parameter types,
+ // where the function would still implicitly override and
+ // cause a compilation error due to the incompatible return
+ // type):
+ OverrideKind k = getOverrideKind(f);
+ if (!((compiler.getSourceManager().isInMainFile(
+ compiler.getSourceManager().getSpellingLoc(
+ dyn_cast<FunctionDecl>(
+ decl->getDeclContext())
+ ->getNameInfo().getLoc()))
+ || f->isDefined() || f->isPure())
+ && k != OverrideKind::MAYBE && rewrite(loc, fbk)))
+ {
report(
DiagnosticsEngine::Warning,
- "VarDecl, use \"bool\" instead of \"sal_Bool\"", loc)
+ ("ParmVarDecl, use \"bool\" instead of"
+ " %0%1"),
+ loc)
+ << decl->getType().getNonReferenceType().getLocalUnqualifiedType()
+ << (k == OverrideKind::MAYBE
+ ? (" (unless this member function overrides a"
+ " dependent base member function, even"
+ " though it is not marked 'override')")
+ : "")
+ << decl->getSourceRange();
+ }
+ }
+ for (auto const dcl: functionDecls_) {
+ auto const decl = dcl.first; auto const fbk = dcl.second;
+ FunctionDecl const * f = decl->getCanonicalDecl();
+ if (ignoredFns.find(f) != ignoredFns.end()) {
+ continue;
+ }
+ SourceLocation loc { compat::getBeginLoc(decl) };
+ SourceLocation l { compiler.getSourceManager().getExpansionLoc(
+ loc) };
+ SourceLocation end { compiler.getSourceManager().getExpansionLoc(
+ decl->getNameInfo().getLoc()) };
+ assert(l.isFileID() && end.isFileID());
+ if (compiler.getSourceManager().isBeforeInTranslationUnit(l, end)) {
+ while (l != end) {
+ unsigned n = Lexer::MeasureTokenLength(
+ l, compiler.getSourceManager(), compiler.getLangOpts());
+ std::string s {
+ compiler.getSourceManager().getCharacterData(l), n };
+ if (s == getName(fbk)) {
+ loc = l;
+ break;
+ }
+ l = l.getLocWithOffset(std::max<unsigned>(n, 1));
+ }
+ }
+ // Only rewrite declarations in include files if a definition is
+ // also seen, to avoid compilation of a definition (in a main file
+ // only processed later) to fail with a "mismatch" error before the
+ // rewriter had a chance to act upon the definition (but use the
+ // heuristic of assuming pure virtual functions do not have
+ // definitions):
+ if (!((compiler.getSourceManager().isInMainFile(
+ compiler.getSourceManager().getSpellingLoc(
+ decl->getNameInfo().getLoc()))
+ || f->isDefined() || f->isPure())
+ && rewrite(loc, fbk)))
+ {
+ report(
+ DiagnosticsEngine::Warning,
+ "use \"bool\" instead of %0 as return type%1",
+ loc)
+ << decl->getReturnType().getNonReferenceType().getLocalUnqualifiedType()
+ << (getOverrideKind(f) == OverrideKind::MAYBE
+ ? (" (unless this member function overrides a dependent"
+ " base member function, even though it is not marked"
+ " 'override')")
+ : "")
<< decl->getSourceRange();
}
}
}
}
-bool SalBool::VisitUnaryAddrOf(UnaryOperator const * op) {
+bool FakeBool::VisitUnaryAddrOf(UnaryOperator const * op) {
+ FunctionAddress::VisitUnaryAddrOf(op);
Expr const * e1 = op->getSubExpr()->IgnoreParenCasts();
- if (isSalBool(e1->getType())) {
- DeclRefExpr const * e2 = dyn_cast<DeclRefExpr>(e1);
- if (e2 != nullptr) {
+ if (isFakeBool(e1->getType()) != FBK_No) {
+ if (DeclRefExpr const * e2 = dyn_cast<DeclRefExpr>(e1)) {
VarDecl const * d = dyn_cast<VarDecl>(e2->getDecl());
if (d != nullptr) {
varDecls_.erase(d);
}
+ } else if (auto const e3 = dyn_cast<MemberExpr>(e1)) {
+ if (auto const d = dyn_cast<FieldDecl>(e3->getMemberDecl())) {
+ fieldDecls_.erase(d);
+ }
}
}
return true;
}
-bool SalBool::VisitCallExpr(CallExpr * expr) {
+bool FakeBool::VisitCallExpr(CallExpr * expr) {
Decl const * d = expr->getCalleeDecl();
FunctionProtoType const * ft = nullptr;
if (d != nullptr) {
@@ -290,7 +605,7 @@ bool SalBool::VisitCallExpr(CallExpr * expr) {
bool b = false;
if (t->isLValueReferenceType()) {
t = t.getNonReferenceType();
- b = !t.isConstQualified() && isSalBool(t);
+ b = !t.isConstQualified() && isFakeBool(t) != FBK_No;
} else if (t->isPointerType()) {
for (;;) {
auto t2 = t->getAs<clang::PointerType>();
@@ -299,16 +614,19 @@ bool SalBool::VisitCallExpr(CallExpr * expr) {
}
t = t2->getPointeeType();
}
- b = isSalBool(t);
+ b = isFakeBool(t) != FBK_No;
}
if (b && i < expr->getNumArgs()) {
- DeclRefExpr * ref = dyn_cast<DeclRefExpr>(
- expr->getArg(i)->IgnoreParenImpCasts());
- if (ref != nullptr) {
+ auto const e1 = expr->getArg(i)->IgnoreParenImpCasts();
+ if (DeclRefExpr * ref = dyn_cast<DeclRefExpr>(e1)) {
VarDecl const * d = dyn_cast<VarDecl>(ref->getDecl());
if (d != nullptr) {
varDecls_.erase(d);
}
+ } else if (auto const e2 = dyn_cast<MemberExpr>(e1)) {
+ if (auto const d = dyn_cast<FieldDecl>(e2->getMemberDecl())) {
+ fieldDecls_.erase(d);
+ }
}
}
}
@@ -316,11 +634,12 @@ bool SalBool::VisitCallExpr(CallExpr * expr) {
return true;
}
-bool SalBool::VisitCStyleCastExpr(CStyleCastExpr * expr) {
+bool FakeBool::VisitCStyleCastExpr(CStyleCastExpr * expr) {
if (ignoreLocation(expr)) {
return true;
}
- if (isSalBool(expr->getType())) {
+ auto const k = isFakeBool(expr->getType());
+ if (k != FBK_No) {
SourceLocation loc { compat::getBeginLoc(expr) };
while (compiler.getSourceManager().isMacroArgExpansion(loc)) {
loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
@@ -328,7 +647,7 @@ bool SalBool::VisitCStyleCastExpr(CStyleCastExpr * expr) {
if (compiler.getSourceManager().isMacroBodyExpansion(loc)) {
StringRef name { Lexer::getImmediateMacroName(
loc, compiler.getSourceManager(), compiler.getLangOpts()) };
- if (name == "sal_False" || name == "sal_True") {
+ if (k == FBK_sal_Bool && (name == "sal_False" || name == "sal_True")) {
auto callLoc = compiler.getSourceManager()
.getImmediateMacroCallerLoc(loc);
if (!isSharedCAndCppCode(callLoc)) {
@@ -345,7 +664,7 @@ bool SalBool::VisitCStyleCastExpr(CStyleCastExpr * expr) {
// arguments to CPPUNIT_ASSERT_EQUAL:
return true;
}
- bool b = name == "sal_True";
+ bool b = k == FBK_sal_Bool && name == "sal_True";
if (rewriter != nullptr) {
auto callSpellLoc = compiler.getSourceManager()
.getSpellingLoc(callLoc);
@@ -380,29 +699,34 @@ bool SalBool::VisitCStyleCastExpr(CStyleCastExpr * expr) {
return true;
}
-bool SalBool::VisitCXXStaticCastExpr(CXXStaticCastExpr * expr) {
+bool FakeBool::VisitCXXStaticCastExpr(CXXStaticCastExpr * expr) {
if (ignoreLocation(expr)) {
return true;
}
- if (isSalBool(expr->getType())
- && !isInSpecialMainFile(
+ auto const k = isFakeBool(expr->getType());
+ if (k == FBK_No) {
+ return true;
+ }
+ if (k == FBK_sal_Bool
+ && isInSpecialMainFile(
compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr))))
{
- report(
- DiagnosticsEngine::Warning,
- "CXXStaticCastExpr, suspicious cast from %0 to %1",
- compat::getBeginLoc(expr))
- << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
- << expr->getType() << expr->getSourceRange();
+ return true;
}
+ report(
+ DiagnosticsEngine::Warning,
+ "CXXStaticCastExpr, suspicious cast from %0 to %1",
+ compat::getBeginLoc(expr))
+ << expr->getSubExpr()->IgnoreParenImpCasts()->getType()
+ << expr->getType() << expr->getSourceRange();
return true;
}
-bool SalBool::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr) {
+bool FakeBool::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr) {
if (ignoreLocation(expr)) {
return true;
}
- if (isSalBool(expr->getType())) {
+ if (isFakeBool(expr->getType()) != FBK_No) {
report(
DiagnosticsEngine::Warning,
"CXXFunctionalCastExpr, suspicious cast from %0 to %1",
@@ -413,11 +737,13 @@ bool SalBool::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr * expr) {
return true;
}
-bool SalBool::VisitImplicitCastExpr(ImplicitCastExpr * expr) {
+bool FakeBool::VisitImplicitCastExpr(ImplicitCastExpr * expr) {
+ FunctionAddress::VisitImplicitCastExpr(expr);
if (ignoreLocation(expr)) {
return true;
}
- if (!isSalBool(expr->getType())) {
+ auto const k = isFakeBool(expr->getType());
+ if (k == FBK_No) {
return true;
}
auto l = compat::getBeginLoc(expr);
@@ -427,7 +753,11 @@ bool SalBool::VisitImplicitCastExpr(ImplicitCastExpr * expr) {
if (compiler.getSourceManager().isMacroBodyExpansion(l)) {
auto n = Lexer::getImmediateMacroName(
l, compiler.getSourceManager(), compiler.getLangOpts());
- if (n == "sal_False" || n == "sal_True") {
+ if ((k == FBK_GLboolean && (n == "GL_FALSE" || n == "GL_TRUE"))
+ || (k == FBK_UBool && (n == "FALSE" || n == "TRUE"))
+ || (k == FBK_jboolean && (n == "JNI_FALSE" || n == "JNI_TRUE"))
+ || (k == FBK_sal_Bool && (n == "sal_False" || n == "sal_True")))
+ {
return true;
}
}
@@ -454,13 +784,13 @@ bool SalBool::VisitImplicitCastExpr(ImplicitCastExpr * expr) {
}
}
report(
- DiagnosticsEngine::Warning, "conversion from %0 to sal_Bool",
+ DiagnosticsEngine::Warning, "conversion from %0 to %1",
compat::getBeginLoc(expr))
- << t << expr->getSourceRange();
+ << t << expr->getType() << expr->getSourceRange();
return true;
}
-bool SalBool::VisitReturnStmt(ReturnStmt const * stmt) {
+bool FakeBool::VisitReturnStmt(ReturnStmt const * stmt) {
// Just enough to avoid warnings in rtl_getUriCharClass (sal/rtl/uri.cxx),
// which has
//
@@ -488,7 +818,7 @@ bool SalBool::VisitReturnStmt(ReturnStmt const * stmt) {
}
t = t2->getPointeeType();
}
- if (!isSalBool(t)) {
+ if (isFakeBool(t) != FBK_sal_Bool) {
return true;
}
auto e2 = dyn_cast<ArraySubscriptExpr>(e->IgnoreParenImpCasts());
@@ -507,92 +837,30 @@ bool SalBool::VisitReturnStmt(ReturnStmt const * stmt) {
return true;
}
-bool SalBool::WalkUpFromParmVarDecl(ParmVarDecl const * decl) {
+bool FakeBool::WalkUpFromParmVarDecl(ParmVarDecl const * decl) {
return VisitParmVarDecl(decl);
}
-bool SalBool::VisitParmVarDecl(ParmVarDecl const * decl) {
+bool FakeBool::VisitParmVarDecl(ParmVarDecl const * decl) {
if (ignoreLocation(decl)) {
return true;
}
- if (isSalBool(decl->getType().getNonReferenceType())) {
+ auto const fbk = isFakeBool(decl->getType().getNonReferenceType());
+ if (fbk != FBK_No) {
FunctionDecl const * f = dyn_cast<FunctionDecl>(decl->getDeclContext());
if (f != nullptr) { // e.g.: typedef sal_Bool (* FuncPtr )( sal_Bool );
f = f->getCanonicalDecl();
- if (!(hasCLanguageLinkageType(f)
- || (isInUnoIncludeFile(f)
- && (!f->isInlined() || f->hasAttr<DeprecatedAttr>()
- || decl->getType()->isReferenceType()
- || hasBoolOverload(f, false)))
- || f->isDeleted() || hasBoolOverload(f, true)))
+ if (isAllRelevantCodeDefined(f)
+ && !(hasCLanguageLinkageType(f)
+ || (fbk == FBK_sal_Bool && isInUnoIncludeFile(f)
+ && (!f->isInlined() || f->hasAttr<DeprecatedAttr>()
+ || decl->getType()->isReferenceType()
+ || hasBoolOverload(f, false)))
+ || f->isDeleted() || hasBoolOverload(f, true)))
{
OverrideKind k = getOverrideKind(f);
if (k != OverrideKind::YES) {
- SourceLocation loc { compat::getBeginLoc(decl) };
- TypeSourceInfo * tsi = decl->getTypeSourceInfo();
- if (tsi != nullptr) {
- SourceLocation l {
- compiler.getSourceManager().getExpansionLoc(
- tsi->getTypeLoc().getBeginLoc()) };
- SourceLocation end {
- compiler.getSourceManager().getExpansionLoc(
- tsi->getTypeLoc().getEndLoc()) };
- assert(l.isFileID() && end.isFileID());
- if (l == end
- || (compiler.getSourceManager()
- .isBeforeInTranslationUnit(l, end)))
- {
- for (;;) {
- unsigned n = Lexer::MeasureTokenLength(
- l, compiler.getSourceManager(),
- compiler.getLangOpts());
- std::string s {
- compiler.getSourceManager().getCharacterData(l),
- n };
- if (s == "sal_Bool") {
- loc = l;
- break;
- }
- if (l == end) {
- break;
- }
- l = l.getLocWithOffset(std::max<unsigned>(n, 1));
- }
- }
- }
- // Only rewrite declarations in include files if a
- // definition is also seen, to avoid compilation of a
- // definition (in a main file only processed later) to fail
- // with a "mismatch" error before the rewriter had a chance
- // to act upon the definition (but use the heuristic of
- // assuming pure virtual functions do not have definitions);
- // also, do not automatically rewrite functions that could
- // implicitly override depend base functions (and thus stop
- // doing so after the rewrite; note that this is less
- // dangerous for return types than for parameter types,
- // where the function would still implicitly override and
- // cause a compilation error due to the incompatible return
- // type):
- if (!((compiler.getSourceManager().isInMainFile(
- compiler.getSourceManager().getSpellingLoc(
- dyn_cast<FunctionDecl>(
- decl->getDeclContext())
- ->getNameInfo().getLoc()))
- || f->isDefined() || f->isPure())
- && k != OverrideKind::MAYBE && rewrite(loc)))
- {
- report(
- DiagnosticsEngine::Warning,
- ("ParmVarDecl, use \"bool\" instead of"
- " \"sal_Bool\"%0"),
- loc)
- << (k == OverrideKind::MAYBE
- ? (" (unless this member function overrides a"
- " dependent base member function, even"
- " though it is not marked 'override')")
- : "")
- << decl->getSourceRange();
- }
+ parmVarDecls_.insert({decl, fbk});
}
}
}
@@ -600,96 +868,92 @@ bool SalBool::VisitParmVarDecl(ParmVarDecl const * decl) {
return true;
}
-bool SalBool::WalkUpFromVarDecl(VarDecl const * decl) {
+bool FakeBool::WalkUpFromVarDecl(VarDecl const * decl) {
return VisitVarDecl(decl);
}
-bool SalBool::VisitVarDecl(VarDecl const * decl) {
+bool FakeBool::VisitVarDecl(VarDecl const * decl) {
if (ignoreLocation(decl)) {
return true;
}
- if (!decl->isExternC()
- && (isSalBool(decl->getType()) || isSalBoolArray(decl->getType()))
- && !isInSpecialMainFile(
- compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(decl))))
+ if (decl->isExternC()) {
+ return true;
+ }
+ auto k = isFakeBool(decl->getType());
+ if (k == FBK_No) {
+ k = isFakeBoolArray(decl->getType());
+ }
+ if (k == FBK_No) {
+ return true;
+ }
+ auto const loc = compat::getBeginLoc(decl);
+ if (k == FBK_sal_Bool
+ && isInSpecialMainFile(
+ compiler.getSourceManager().getSpellingLoc(loc)))
+ {
+ return true;
+ }
+ auto l = loc;
+ while (compiler.getSourceManager().isMacroArgExpansion(l)) {
+ l = compiler.getSourceManager().getImmediateMacroCallerLoc(l);
+ }
+ if (compiler.getSourceManager().isMacroBodyExpansion(l)
+ && isSharedCAndCppCode(compiler.getSourceManager().getImmediateMacroCallerLoc(l)))
{
- varDecls_.insert(decl);
+ return true;
}
+ varDecls_.insert({decl, k});
return true;
}
-bool SalBool::WalkUpFromFieldDecl(FieldDecl const * decl) {
+bool FakeBool::WalkUpFromFieldDecl(FieldDecl const * decl) {
return VisitFieldDecl(decl);
}
-bool SalBool::VisitFieldDecl(FieldDecl const * decl) {
+bool FakeBool::VisitFieldDecl(FieldDecl const * decl) {
if (ignoreLocation(decl)) {
return true;
}
- if ((isSalBool(decl->getType()) || isSalBoolArray(decl->getType()))
- && !isInSpecialMainFile(
+ auto k = isFakeBool(decl->getType());
+ if (k == FBK_No) {
+ k = isFakeBoolArray(decl->getType());
+ }
+ if (k == FBK_No) {
+ return true;
+ }
+ if (!isAllRelevantCodeDefined(decl)) {
+ return true;
+ }
+ if (k == FBK_sal_Bool
+ && isInSpecialMainFile(
compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(decl))))
{
- TagDecl const * td = dyn_cast<TagDecl>(decl->getDeclContext());
- assert(td != nullptr);
- if (!(((td->isStruct() || td->isUnion()) && td->isExternCContext())
- || isInUnoIncludeFile(
- compiler.getSourceManager().getSpellingLoc(
- decl->getLocation()))))
- {
- SourceLocation loc { compat::getBeginLoc(decl) };
- TypeSourceInfo * tsi = decl->getTypeSourceInfo();
- if (tsi != nullptr) {
- SourceLocation l {
- compiler.getSourceManager().getExpansionLoc(
- tsi->getTypeLoc().getBeginLoc()) };
- SourceLocation end {
- compiler.getSourceManager().getExpansionLoc(
- tsi->getTypeLoc().getEndLoc()) };
- assert(l.isFileID() && end.isFileID());
- if (l == end
- || compiler.getSourceManager().isBeforeInTranslationUnit(
- l, end))
- {
- for (;;) {
- unsigned n = Lexer::MeasureTokenLength(
- l, compiler.getSourceManager(),
- compiler.getLangOpts());
- std::string s {
- compiler.getSourceManager().getCharacterData(l),
- n };
- if (s == "sal_Bool") {
- loc = l;
- break;
- }
- if (l == end) {
- break;
- }
- l = l.getLocWithOffset(std::max<unsigned>(n, 1));
- }
- }
- }
- if (!rewrite(loc)) {
- report(
- DiagnosticsEngine::Warning,
- "FieldDecl, use \"bool\" instead of \"sal_Bool\"", loc)
- << decl->getSourceRange();
- }
- }
+ return true;
+ }
+ TagDecl const * td = dyn_cast<TagDecl>(decl->getDeclContext());
+ assert(td != nullptr);
+ if (!(((td->isStruct() || td->isUnion()) && td->isExternCContext())
+ || isInUnoIncludeFile(
+ compiler.getSourceManager().getSpellingLoc(
+ decl->getLocation()))))
+ {
+ fieldDecls_.insert({decl, k});
}
return true;
}
-bool SalBool::WalkUpFromFunctionDecl(FunctionDecl const * decl) {
+bool FakeBool::WalkUpFromFunctionDecl(FunctionDecl const * decl) {
return VisitFunctionDecl(decl);
}
-bool SalBool::VisitFunctionDecl(FunctionDecl const * decl) {
+bool FakeBool::VisitFunctionDecl(FunctionDecl const * decl) {
if (ignoreLocation(decl)) {
return true;
}
- if (isSalBool(decl->getReturnType().getNonReferenceType())
- && !(decl->isDeletedAsWritten() && isa<CXXConversionDecl>(decl)))
+ auto const fbk = isFakeBool(decl->getReturnType().getNonReferenceType());
+ if (fbk != FBK_No
+ && !(decl->isDeletedAsWritten() && isa<CXXConversionDecl>(decl))
+ && isAllRelevantCodeDefined(decl))
{
FunctionDecl const * f = decl->getCanonicalDecl();
OverrideKind k = getOverrideKind(f);
@@ -698,68 +962,28 @@ bool SalBool::VisitFunctionDecl(FunctionDecl const * decl) {
|| (isInUnoIncludeFile(f)
&& (!f->isInlined() || f->hasAttr<DeprecatedAttr>()))))
{
- SourceLocation loc { compat::getBeginLoc(decl) };
- SourceLocation l { compiler.getSourceManager().getExpansionLoc(
- loc) };
- SourceLocation end { compiler.getSourceManager().getExpansionLoc(
- decl->getNameInfo().getLoc()) };
- assert(l.isFileID() && end.isFileID());
- if (compiler.getSourceManager().isBeforeInTranslationUnit(l, end)) {
- while (l != end) {
- unsigned n = Lexer::MeasureTokenLength(
- l, compiler.getSourceManager(), compiler.getLangOpts());
- std::string s {
- compiler.getSourceManager().getCharacterData(l), n };
- if (s == "sal_Bool") {
- loc = l;
- break;
- }
- l = l.getLocWithOffset(std::max<unsigned>(n, 1));
- }
- }
- // Only rewrite declarations in include files if a definition is
- // also seen, to avoid compilation of a definition (in a main file
- // only processed later) to fail with a "mismatch" error before the
- // rewriter had a chance to act upon the definition (but use the
- // heuristic of assuming pure virtual functions do not have
- // definitions):
- if (!((compiler.getSourceManager().isInMainFile(
- compiler.getSourceManager().getSpellingLoc(
- decl->getNameInfo().getLoc()))
- || f->isDefined() || f->isPure())
- && rewrite(loc)))
- {
- report(
- DiagnosticsEngine::Warning,
- "use \"bool\" instead of \"sal_Bool\" as return type%0",
- loc)
- << (k == OverrideKind::MAYBE
- ? (" (unless this member function overrides a dependent"
- " base member function, even though it is not marked"
- " 'override')")
- : "")
- << decl->getSourceRange();
- }
+ functionDecls_.insert({decl, fbk});
}
}
return true;
}
-bool SalBool::VisitValueDecl(ValueDecl const * decl) {
+bool FakeBool::VisitValueDecl(ValueDecl const * decl) {
if (ignoreLocation(decl)) {
return true;
}
- if (isSalBool(decl->getType()) && !rewrite(compat::getBeginLoc(decl))) {
+ auto const k = isFakeBool(decl->getType());
+ if (k != FBK_No && !rewrite(compat::getBeginLoc(decl), k)) {
report(
DiagnosticsEngine::Warning,
- "ValueDecl, use \"bool\" instead of \"sal_Bool\"",
+ "ValueDecl, use \"bool\" instead of %0",
compat::getBeginLoc(decl))
- << decl->getSourceRange();
+ << decl->getType() << decl->getSourceRange();
}
return true;
}
-bool SalBool::TraverseStaticAssertDecl(StaticAssertDecl * decl) {
+bool FakeBool::TraverseStaticAssertDecl(StaticAssertDecl * decl) {
// Ignore special code like
//
// static_cast<sal_Bool>(true) == sal_True
@@ -772,7 +996,7 @@ bool SalBool::TraverseStaticAssertDecl(StaticAssertDecl * decl) {
|| RecursiveASTVisitor::TraverseStaticAssertDecl(decl);
}
-bool SalBool::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
+bool FakeBool::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
assert(externCContexts_ != std::numeric_limits<unsigned int>::max()); //TODO
++externCContexts_;
bool ret = RecursiveASTVisitor::TraverseLinkageSpecDecl(decl);
@@ -781,7 +1005,7 @@ bool SalBool::TraverseLinkageSpecDecl(LinkageSpecDecl * decl) {
return ret;
}
-bool SalBool::isFromCIncludeFile(SourceLocation spellingLocation) const {
+bool FakeBool::isFromCIncludeFile(SourceLocation spellingLocation) const {
return !compiler.getSourceManager().isInMainFile(spellingLocation)
&& (StringRef(
compiler.getSourceManager().getPresumedLoc(spellingLocation)
@@ -789,7 +1013,7 @@ bool SalBool::isFromCIncludeFile(SourceLocation spellingLocation) const {
.endswith(".h"));
}
-bool SalBool::isSharedCAndCppCode(SourceLocation location) const {
+bool FakeBool::isSharedCAndCppCode(SourceLocation location) const {
// Assume that code is intended to be shared between C and C++ if it comes
// from an include file ending in .h, and is either in an extern "C" context
// or the body of a macro definition:
@@ -799,7 +1023,7 @@ bool SalBool::isSharedCAndCppCode(SourceLocation location) const {
|| compiler.getSourceManager().isMacroBodyExpansion(location));
}
-bool SalBool::isInSpecialMainFile(SourceLocation spellingLocation) const {
+bool FakeBool::isInSpecialMainFile(SourceLocation spellingLocation) const {
if (!compiler.getSourceManager().isInMainFile(spellingLocation)) {
return false;
}
@@ -809,7 +1033,7 @@ bool SalBool::isInSpecialMainFile(SourceLocation spellingLocation) const {
// TODO: the offset checks
}
-bool SalBool::rewrite(SourceLocation location) {
+bool FakeBool::rewrite(SourceLocation location, FakeBoolKind kind) {
if (rewriter != nullptr) {
//TODO: "::sal_Bool" -> "bool", not "::bool"
SourceLocation loc { compiler.getSourceManager().getExpansionLoc(
@@ -817,7 +1041,7 @@ bool SalBool::rewrite(SourceLocation location) {
unsigned n = Lexer::MeasureTokenLength(
loc, compiler.getSourceManager(), compiler.getLangOpts());
if (std::string(compiler.getSourceManager().getCharacterData(loc), n)
- == "sal_Bool")
+ == getName(kind))
{
return replaceText(loc, n, "bool");
}
@@ -825,7 +1049,7 @@ bool SalBool::rewrite(SourceLocation location) {
return false;
}
-loplugin::Plugin::Registration<SalBool> X("salbool", true);
+loplugin::Plugin::Registration<FakeBool> X("fakebool", true);
}
diff --git a/compilerplugins/clang/test/salbool.cxx b/compilerplugins/clang/test/fakebool.cxx
index da861afe73be..26b5d7e2f791 100644
--- a/compilerplugins/clang/test/salbool.cxx
+++ b/compilerplugins/clang/test/fakebool.cxx
@@ -11,8 +11,12 @@
#include <sal/types.h>
+namespace {
+
struct S {
- sal_Bool b; // expected-error {{FieldDecl, use "bool" instead of "sal_Bool" [loplugin:salbool]}}
+ sal_Bool b; // expected-error {{FieldDecl, use "bool" instead of 'sal_Bool' (aka 'unsigned char') [loplugin:fakebool]}}
};
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */