diff options
Diffstat (limited to 'compilerplugins/clang/plugin.cxx')
-rw-r--r-- | compilerplugins/clang/plugin.cxx | 225 |
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(); |