From 7ca0a75b85172c70dfa8994ed233c1b62ef94c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20Gr=C3=A4sman?= Date: Wed, 13 Jul 2022 21:04:00 +0200 Subject: [PATCH] [clang compat] Harden handling of 'final' specifier Clang 9f57b65a272817752aa00e2fb94154e6eed1d0ec sometimes prints the 'final' keyword twice for a declaration (tracking bug: https://github.com/llvm/llvm-project/issues/56517). This triggered several bugs in MungedForwardDeclareLineForTemplates: * Only removed first 'final' * Removed one character too many (sizeof(char[]) includes the trailing NUL) Instead, replace all occurrences of 'final' and do it first so the stripping of superclasses and body also get rid of trailing spaces. This should now work even if the upstream bug is fixed. --- iwyu_output.cc | 17 ++++++++--------- iwyu_string_util.h | 8 ++++++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/iwyu_output.cc b/iwyu_output.cc index e67474c..3f364ac 100644 --- a/iwyu_output.cc +++ b/iwyu_output.cc @@ -457,25 +457,24 @@ string MungedForwardDeclareLineForTemplates(const TemplateDecl* decl) { raw_string_ostream ostream(line); decl->print(ostream); // calls DeclPrinter line = ostream.str(); + + // Remove "final" specifier which isn't needed for forward + // declarations. + ReplaceAll(&line, " final ", " "); + // Get rid of the superclasses, if any (this will nix the body too). line = Split(line, " :", 2)[0]; // Get rid of the template body, if any (true if no superclasses). line = Split(line, " {", 2)[0]; - // Remove "final" specifier which isn't needed for forward - // declarations. - const char kFinalSpecifier[] = " final "; - string::size_type final_pos = line.find(kFinalSpecifier); - if (final_pos != string::npos) { - line.replace(final_pos, sizeof(kFinalSpecifier), " "); - } - // The template name is now the last word on the line. Replace it // by its fully-qualified form. const string::size_type name = line.rfind(' '); CHECK_(name != string::npos && "Unexpected printable template-type"); - return PrintForwardDeclare(decl, line.substr(0, name), GlobalFlags().cxx17ns); + line = line.substr(0, name); + + return PrintForwardDeclare(decl, line, GlobalFlags().cxx17ns); } string MungedForwardDeclareLine(const NamedDecl* decl) { diff --git a/iwyu_string_util.h b/iwyu_string_util.h index 78c2dc7..71e4d56 100644 --- a/iwyu_string_util.h +++ b/iwyu_string_util.h @@ -119,6 +119,14 @@ inline void StripWhiteSpace(string* str) { StripWhiteSpaceRight(str); } +inline void ReplaceAll(std::string* str, const std::string& from, + const std::string& to) { + for (size_t pos = str->find(from); pos != std::string::npos; + pos = str->find(from, pos + to.length())) { + str->replace(pos, from.length(), to); + } +} + // This is the same as split() in Python. If max_segs is 0, there's // no limit on the number of the generated segments. inline vector Split(