summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compilerplugins/clang/plugin.cxx177
-rw-r--r--compilerplugins/clang/plugin.hxx4
-rw-r--r--compilerplugins/clang/redundantfcast.cxx30
-rw-r--r--connectivity/source/drivers/firebird/DatabaseMetaData.cxx1
-rw-r--r--drawinglayer/source/tools/emfppen.cxx4
-rw-r--r--sc/source/core/data/table2.cxx2
6 files changed, 200 insertions, 18 deletions
diff --git a/compilerplugins/clang/plugin.cxx b/compilerplugins/clang/plugin.cxx
index 8bf5b4333bdc..727eda589548 100644
--- a/compilerplugins/clang/plugin.cxx
+++ b/compilerplugins/clang/plugin.cxx
@@ -128,6 +128,183 @@ DiagnosticBuilder Plugin::report( DiagnosticsEngine::Level level, StringRef mess
return handler.report( level, name, message, compiler, loc );
}
+bool Plugin::suppressWarningAt(SourceLocation location) const {
+ auto const start = compiler.getSourceManager().getSpellingLoc(location);
+ auto const startInfo = compiler.getSourceManager().getDecomposedLoc(start);
+ auto invalid = false;
+ auto const buf = compiler.getSourceManager().getBufferData(startInfo.first, &invalid);
+ if (invalid) {
+ if (isDebugMode()) {
+ report(DiagnosticsEngine::Fatal, "failed to getBufferData", start);
+ }
+ return false;
+ }
+ auto const label = std::string("[-loplugin:").append(name).append("]");
+ // Look back to the beginning of the previous line:
+ auto loc = start;
+ auto locInfo = startInfo;
+ auto cur = loc;
+ enum class State { Normal, Slash, Comment };
+ auto state = State::Normal;
+ auto newlines = 0;
+ for (auto prev = cur;;) {
+ auto prevInfo = compiler.getSourceManager().getDecomposedLoc(prev);
+ if (prev == compiler.getSourceManager().getLocForStartOfFile(prevInfo.first)) {
+ if (state == State::Comment && isDebugMode()) {
+ report(
+ DiagnosticsEngine::Fatal,
+ "beginning of file while looking for beginning of comment", prev);
+ }
+ break;
+ }
+ Token tok;
+ if (Lexer::getRawToken(
+ Lexer::GetBeginningOfToken(
+ prev.getLocWithOffset(-1), compiler.getSourceManager(), compiler.getLangOpts()),
+ tok, compiler.getSourceManager(), compiler.getLangOpts(), true))
+ {
+ if (isDebugMode()) {
+ report(
+ DiagnosticsEngine::Fatal, "failed to getRawToken",
+ prev.getLocWithOffset(-1));
+ }
+ break;
+ }
+ if (tok.getLocation() == cur) {
+ // Keep walking back, character by character, through whitespace preceding the current
+ // token, which Clang treats as nominally belonging to that token (so the above
+ // Lexer::getRawToken/Lexer::GetBeginningOfToken will have produced just the same tok
+ // again):
+ prev = prev.getLocWithOffset(-1);
+ continue;
+ }
+ cur = tok.getLocation();
+ prev = cur;
+ if (state == State::Comment) {
+ // Lexer::GetBeginningOfToken (at least towards Clang 15, still) only re-scans from the
+ // start of the current line, so if we saw the end of a multi-line /*...*/ comment, we
+ // saw that as individual '/' and '*' faux-tokens, at which point we must (hopefully?)
+ // actually be at the end of such a multi-line comment, so we keep walking back to the
+ // first '/*' we encounter (TODO: which still need not be the real start of the comment,
+ // if the comment contains embedded '/*', but we could determine that only if we
+ // re-scanned from the start of the file):
+ if (!tok.is(tok::comment)) {
+ continue;
+ }
+ SmallVector<char, 256> tmp;
+ bool invalid = false;
+ auto const spell = Lexer::getSpelling(
+ prev, tmp, compiler.getSourceManager(), compiler.getLangOpts(), &invalid);
+ if (invalid) {
+ if (isDebugMode()) {
+ report(DiagnosticsEngine::Fatal, "failed to getSpelling", prev);
+ }
+ } else if (!spell.startswith("/*")) {
+ continue;
+ }
+ }
+ prevInfo = compiler.getSourceManager().getDecomposedLoc(prev);
+ auto const end = prev.getLocWithOffset(tok.getLength());
+ auto const endInfo = compiler.getSourceManager().getDecomposedLoc(end);
+ assert(prevInfo.first == endInfo .first);
+ assert(prevInfo.second <= endInfo.second);
+ assert(endInfo.first == locInfo.first);
+ // Whitespace between tokens is found at the end of prev, from end to loc (unless this is a
+ // multi-line comment, in which case the whitespace has already been inspected as the
+ // whitespace following the comment's final '/' faux-token):
+ StringRef ws;
+ if (state != State::Comment) {
+ assert(endInfo.second <= locInfo.second);
+ ws = buf.substr(endInfo.second, locInfo.second - endInfo.second);
+ }
+ for (std::size_t i = 0;;) {
+ auto const j = ws.find('\n', i);
+ if (j == StringRef::npos) {
+ break;
+ }
+ ++newlines;
+ if (newlines == 2) {
+ break;
+ }
+ i = j + 1;
+ }
+ if (newlines == 2) {
+ break;
+ }
+ auto str = buf.substr(prevInfo.second, endInfo.second - prevInfo.second);
+ if (tok.is(tok::comment) && str.contains(label)) {
+ return true;
+ }
+ for (std::size_t i = 0;;) {
+ auto const j = str.find('\n', i);
+ if (j == StringRef::npos) {
+ break;
+ }
+ ++newlines;
+ if (newlines == 2) {
+ break;
+ }
+ i = j + 1;
+ }
+ if (newlines == 2) {
+ break;
+ }
+ loc = prev;
+ locInfo = prevInfo;
+ switch (state) {
+ case State::Normal:
+ if (tok.is(tok::slash)) {
+ state = State::Slash;
+ }
+ break;
+ case State::Slash:
+ state = tok.is(tok::star) && ws.empty() ? State::Comment : State::Normal;
+ //TODO: check for "ws is only folding whitespace" rather than for `ws.empty()` (but
+ // then, we must not count newlines in that whitespace twice, first as part of the
+ // whitespace following the comment's semi-final '*' faux-token and then as part of
+ // the comment token's content)
+ break;
+ case State::Comment:
+ state = State::Normal;
+ }
+ }
+ // Look forward to the end of the current line:
+ loc = start;
+ locInfo = startInfo;
+ for (;;) {
+ Token tok;
+ if (Lexer::getRawToken(loc, tok, compiler.getSourceManager(), compiler.getLangOpts(), true))
+ {
+ if (isDebugMode()) {
+ report(DiagnosticsEngine::Fatal, "failed to getRawToken", loc);
+ }
+ break;
+ }
+ // Whitespace between tokens is found at the beginning, from loc to beg:
+ auto const beg = tok.getLocation();
+ auto const begInfo = compiler.getSourceManager().getDecomposedLoc(beg);
+ assert(begInfo.first == locInfo.first);
+ assert(begInfo.second >= locInfo.second);
+ if (buf.substr(locInfo.second, begInfo.second - locInfo.second).contains('\n')) {
+ break;
+ }
+ auto const next = beg.getLocWithOffset(tok.getLength());
+ auto const nextInfo = compiler.getSourceManager().getDecomposedLoc(next);
+ assert(nextInfo.first == begInfo.first);
+ assert(nextInfo.second >= begInfo.second);
+ auto const str = buf.substr(begInfo.second, nextInfo.second - begInfo.second);
+ if (tok.is(tok::comment) && str.contains(label)) {
+ return true;
+ }
+ if (tok.is(tok::eof) || str.contains('\n')) {
+ break;
+ }
+ loc = next;
+ locInfo = nextInfo;
+ }
+ return false;
+}
+
void normalizeDotDotInFilePath( std::string & s )
{
for (std::string::size_type i = 0;;)
diff --git a/compilerplugins/clang/plugin.hxx b/compilerplugins/clang/plugin.hxx
index 86ca8f825e02..cd272dc520f8 100644
--- a/compilerplugins/clang/plugin.hxx
+++ b/compilerplugins/clang/plugin.hxx
@@ -65,6 +65,10 @@ public:
enum { isSharedPlugin = false };
protected:
DiagnosticBuilder report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc = SourceLocation()) const;
+ // Look at the line containing location and the previous line for any comments that overlap
+ // either of those two lines and that contain "[-loplugin:<name>]" (with the name of this
+ // plugin), indicating that warnings from this plugin should be suppressed here:
+ bool suppressWarningAt(SourceLocation location) const;
bool ignoreLocation( SourceLocation loc ) const
{ return handler.ignoreLocation(loc); }
bool ignoreLocation( const Decl* decl ) const;
diff --git a/compilerplugins/clang/redundantfcast.cxx b/compilerplugins/clang/redundantfcast.cxx
index aed71e8783f9..e78f2cf976ab 100644
--- a/compilerplugins/clang/redundantfcast.cxx
+++ b/compilerplugins/clang/redundantfcast.cxx
@@ -56,9 +56,15 @@ public:
return true;
}
if (m_Seen.insert(cxxFunctionalCastExpr->getExprLoc()).second)
+ {
+ if (suppressWarningAt(cxxFunctionalCastExpr->getBeginLoc()))
+ {
+ return true;
+ }
report(DiagnosticsEngine::Warning, "redundant functional cast from %0 to %1",
cxxFunctionalCastExpr->getExprLoc())
<< t2 << t1 << cxxFunctionalCastExpr->getSourceRange();
+ }
return true;
}
@@ -310,9 +316,15 @@ public:
return true;
if (m_Seen.insert(expr->getExprLoc()).second)
+ {
+ if (suppressWarningAt(expr->getBeginLoc()))
+ {
+ return true;
+ }
report(DiagnosticsEngine::Warning, "redundant functional cast from %0 to %1",
expr->getExprLoc())
<< t2 << t1 << expr->getSourceRange();
+ }
return true;
}
@@ -320,24 +332,6 @@ public:
{
if (!compiler.getLangOpts().CPlusPlus)
return false;
- std::string fn = handler.getMainFileName().str();
- loplugin::normalizeDotDotInFilePath(fn);
- // necessary on some other platforms
- if (fn == SRCDIR "/sal/osl/unx/socket.cxx")
- return false;
- // compile-time check of constant
- if (fn == SRCDIR "/bridges/source/jni_uno/jni_bridge.cxx")
- return false;
- // TODO constructing a temporary to pass to a && param
- if (fn == SRCDIR "/sc/source/ui/view/viewfunc.cxx"
- || fn == SRCDIR "/sc/source/core/data/table2.cxx")
- return false;
- // tdf#145203: FIREBIRD cannot create a table
- if (fn == SRCDIR "/connectivity/source/drivers/firebird/DatabaseMetaData.cxx")
- return false;
- // false positive during using constructor drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw })
- if (fn == SRCDIR "/drawinglayer/source/tools/emfppen.cxx")
- return false;
return true;
}
diff --git a/connectivity/source/drivers/firebird/DatabaseMetaData.cxx b/connectivity/source/drivers/firebird/DatabaseMetaData.cxx
index ab14d957f936..cfee4f41b81d 100644
--- a/connectivity/source/drivers/firebird/DatabaseMetaData.cxx
+++ b/connectivity/source/drivers/firebird/DatabaseMetaData.cxx
@@ -1026,6 +1026,7 @@ uno::Reference< XResultSet > SAL_CALL ODatabaseMetaData::getTypeInfo()
tmp.push_back(aRow);
return tmp;
}();
+ // [-loplugin:redundantfcast] false positive:
pResultSet->setRows(ODatabaseMetaDataResultSet::ORows(aResults));
return pResultSet;
}
diff --git a/drawinglayer/source/tools/emfppen.cxx b/drawinglayer/source/tools/emfppen.cxx
index 7d999d61cc8b..b927ac186174 100644
--- a/drawinglayer/source/tools/emfppen.cxx
+++ b/drawinglayer/source/tools/emfppen.cxx
@@ -219,12 +219,16 @@ namespace emfplushelper
switch (dashStyle)
{
case EmfPlusLineStyleDash:
+ // [-loplugin:redundantfcast] false positive:
return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw });
case EmfPlusLineStyleDot:
+ // [-loplugin:redundantfcast] false positive:
return drawinglayer::attribute::StrokeAttribute({ pw, pw });
case EmfPlusLineStyleDashDot:
+ // [-loplugin:redundantfcast] false positive:
return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw, pw, pw });
case EmfPlusLineStyleDashDotDot:
+ // [-loplugin:redundantfcast] false positive:
return drawinglayer::attribute::StrokeAttribute({ 3 * pw, pw, pw, pw, pw, pw });
}
}
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index e9b5bb371a85..55c11a3ce065 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -2891,6 +2891,7 @@ void ScTable::SetAttrEntries( SCCOL nStartCol, SCCOL nEndCol, std::vector<ScAttr
// If we would like set all columns to same attrs, then change only attrs for not existing columns
nEndCol = aCol.size() - 1;
for (SCCOL i = nStartCol; i <= nEndCol; i++)
+ // [-loplugin:redundantfcast] false positive:
aCol[i].SetAttrEntries( std::vector<ScAttrEntry>(vNewData));
aDefaultColData.SetAttrEntries(std::move(vNewData));
}
@@ -2904,6 +2905,7 @@ void ScTable::SetAttrEntries( SCCOL nStartCol, SCCOL nEndCol, std::vector<ScAttr
{
CreateColumnIfNotExists( nEndCol );
for (SCCOL i = nStartCol; i < nEndCol; i++) // all but last need a copy
+ // [-loplugin:redundantfcast] false positive:
aCol[i].SetAttrEntries( std::vector<ScAttrEntry>(vNewData));
aCol[nEndCol].SetAttrEntries( std::move(vNewData));
}