summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/plugin.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang/plugin.cxx')
-rw-r--r--compilerplugins/clang/plugin.cxx225
1 files changed, 214 insertions, 11 deletions
diff --git a/compilerplugins/clang/plugin.cxx b/compilerplugins/clang/plugin.cxx
index edf91f33d19b..8d8207d30437 100644
--- a/compilerplugins/clang/plugin.cxx
+++ b/compilerplugins/clang/plugin.cxx
@@ -15,6 +15,7 @@
#include <cstddef>
#include <string>
+#include <clang/AST/ParentMapContext.h>
#include <clang/Basic/FileManager.h>
#include <clang/Lex/Lexer.h>
@@ -24,10 +25,6 @@
#include "pluginhandler.hxx"
#include "check.hxx"
-#if CLANG_VERSION >= 110000
-#include "clang/AST/ParentMapContext.h"
-#endif
-
/*
Base classes for plugin actions.
*/
@@ -38,7 +35,7 @@ namespace {
Expr const * skipImplicit(Expr const * expr) {
if (auto const e = dyn_cast<MaterializeTemporaryExpr>(expr)) {
- expr = compat::getSubExpr(e)->IgnoreImpCasts();
+ expr = e->getSubExpr()->IgnoreImpCasts();
}
if (auto const e = dyn_cast<CXXBindTemporaryExpr>(expr)) {
expr = e->getSubExpr();
@@ -97,10 +94,14 @@ bool structurallyIdentical(Stmt const * stmt1, Stmt const * stmt2) {
break;
case Stmt::MaterializeTemporaryExprClass:
case Stmt::CXXBindTemporaryExprClass:
+ case Stmt::CXXDefaultArgExprClass:
case Stmt::ParenExprClass:
break;
case Stmt::CXXNullPtrLiteralExprClass:
return true;
+ case Stmt::StringLiteralClass:
+ return cast<clang::StringLiteral>(stmt1)->getBytes()
+ == cast<clang::StringLiteral>(stmt2)->getBytes();
default:
// Conservatively assume non-identical for expressions that don't happen for us in practice
// when compiling the LO code base (and for which the above set of supported classes would
@@ -131,6 +132,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 (!compat::starts_with(spell, "/*")) {
+ 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;;)
@@ -263,7 +441,7 @@ StringRef Plugin::getFilenameOfLocation(SourceLocation spellingLocation) const
return fn;
}
#if !defined _WIN32
- assert(fn.startswith("/") || fn == "<stdin>");
+ assert(compat::starts_with(fn, "/") || fn == "<stdin>");
#endif
s_Mode = fn == "<stdin>" ? STDIN : GOOD;
return getFilenameOfLocation(spellingLocation);
@@ -369,6 +547,32 @@ bool Plugin::containsPreprocessingConditionalInclusion(SourceRange range)
return false;
}
+bool Plugin::containsComment(SourceRange range)
+{
+ SourceManager& SM = compiler.getSourceManager();
+ SourceLocation startLoc = range.getBegin();
+ SourceLocation endLoc = range.getEnd();
+ char const* p1 = SM.getCharacterData(startLoc);
+ char const* p2 = SM.getCharacterData(endLoc);
+ p2 += Lexer::MeasureTokenLength(endLoc, SM, compiler.getLangOpts());
+
+ // when doing 'make solenv.check' we don't want the special comments in the
+ // unit test files to trigger this check
+ constexpr char const comment0[] = "// expected-error";
+ if (std::search(p1, p2, comment0, comment0 + strlen(comment0)) != p2)
+ return false;
+
+ // check for comments
+ constexpr char const comment1[] = "/*";
+ constexpr char const comment2[] = "//";
+ if (std::search(p1, p2, comment1, comment1 + strlen(comment1)) != p2)
+ return true;
+ if (std::search(p1, p2, comment2, comment2 + strlen(comment2)) != p2)
+ return true;
+
+ return false;
+}
+
Plugin::IdenticalDefaultArgumentsResult Plugin::checkIdenticalDefaultArguments(
Expr const * argument1, Expr const * argument2)
{
@@ -658,9 +862,8 @@ bool RewritePlugin::wouldRewriteWorkdir(SourceLocation loc)
if (loc.isInvalid() || loc.isMacroID()) {
return false;
}
- return
- getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(loc))
- .startswith(WORKDIR "/");
+ return compat::starts_with(
+ getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(loc)), WORKDIR "/");
}
bool RewritePlugin::reportEditFailure( SourceLocation loc )
@@ -714,7 +917,7 @@ bool hasPathnamePrefix(StringRef pathname, StringRef prefix)
{
return checkPathname(
pathname, prefix,
- [](StringRef p, StringRef a) { return p.startswith(a); });
+ [](StringRef p, StringRef a) { return compat::starts_with(p, a); });
}
bool isSamePathname(StringRef pathname, StringRef other)
@@ -809,7 +1012,7 @@ int derivedFromCount(QualType subclassQt, QualType baseclassQt)
// a variable declared in an 'extern "..." {...}'-style linkage-specification as
// if it contained the 'extern' specifier:
bool hasExternalLinkage(VarDecl const * decl) {
- if (decl->getLinkageAndVisibility().getLinkage() != ExternalLinkage) {
+ if (decl->getLinkageAndVisibility().getLinkage() != compat::Linkage::External) {
return false;
}
for (auto ctx = decl->getLexicalDeclContext();