Fix readability-const-return-type identifying the wrong `const` token
authorIlya Mirsky <ilya.mirsky@ericsson.com>
Tue, 24 Dec 2019 15:10:01 +0000 (10:10 -0500)
committerAaron Ballman <aaron@aaronballman.com>
Tue, 24 Dec 2019 15:10:01 +0000 (10:10 -0500)
Replace tidy::utils::lexer::getConstQualifyingToken with a corrected and also
generalized to other qualifiers variant - getQualifyingToken.

Fixes PR44326

clang-tools-extra/clang-tidy/readability/ConstReturnTypeCheck.cpp
clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
clang-tools-extra/clang-tidy/utils/LexerUtils.h
clang-tools-extra/test/clang-tidy/checkers/readability-const-return-type.cpp

index 0f237ec..d987886 100644 (file)
@@ -47,8 +47,8 @@ findConstToRemove(const FunctionDecl *Def,
   if (FileRange.isInvalid())
     return None;
 
-  return utils::lexer::getConstQualifyingToken(FileRange, *Result.Context,
-                                               *Result.SourceManager);
+  return utils::lexer::getQualifyingToken(
+      tok::kw_const, FileRange, *Result.Context, *Result.SourceManager);
 }
 
 namespace {
index e80fda6..17838fe 100644 (file)
@@ -102,15 +102,20 @@ bool rangeContainsExpansionsOrDirectives(SourceRange Range,
   return false;
 }
 
-llvm::Optional<Token> getConstQualifyingToken(CharSourceRange Range,
-                                              const ASTContext &Context,
-                                              const SourceManager &SM) {
+llvm::Optional<Token> getQualifyingToken(tok::TokenKind TK,
+                                         CharSourceRange Range,
+                                         const ASTContext &Context,
+                                         const SourceManager &SM) {
+  assert((TK == tok::kw_const || TK == tok::kw_volatile ||
+          TK == tok::kw_restrict) &&
+         "TK is not a qualifier keyword");
   std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Range.getBegin());
   StringRef File = SM.getBufferData(LocInfo.first);
   Lexer RawLexer(SM.getLocForStartOfFile(LocInfo.first), Context.getLangOpts(),
                  File.begin(), File.data() + LocInfo.second, File.end());
-  llvm::Optional<Token> FirstConstTok;
-  Token LastTokInRange;
+  llvm::Optional<Token> LastMatchBeforeTemplate;
+  llvm::Optional<Token> LastMatchAfterTemplate;
+  bool SawTemplate = false;
   Token Tok;
   while (!RawLexer.LexFromRawLexer(Tok) &&
          Range.getEnd() != Tok.getLocation() &&
@@ -121,13 +126,19 @@ llvm::Optional<Token> getConstQualifyingToken(CharSourceRange Range,
       Tok.setIdentifierInfo(&Info);
       Tok.setKind(Info.getTokenID());
     }
-    if (Tok.is(tok::kw_const) && !FirstConstTok)
-      FirstConstTok = Tok;
-    LastTokInRange = Tok;
+    if (Tok.is(tok::less))
+      SawTemplate = true;
+    else if (Tok.isOneOf(tok::greater, tok::greatergreater))
+      LastMatchAfterTemplate = None;
+    else if (Tok.is(TK)) {
+      if (SawTemplate)
+        LastMatchAfterTemplate = Tok;
+      else
+        LastMatchBeforeTemplate = Tok;
+    }
   }
-  // If the last token in the range is a `const`, then it const qualifies the
-  // type.  Otherwise, the first `const` token, if any, is the qualifier.
-  return LastTokInRange.is(tok::kw_const) ? LastTokInRange : FirstConstTok;
+  return LastMatchAfterTemplate != None ? LastMatchAfterTemplate
+                                        : LastMatchBeforeTemplate;
 }
 } // namespace lexer
 } // namespace utils
index 2c4a251..fcf9ada 100644 (file)
@@ -92,13 +92,15 @@ bool rangeContainsExpansionsOrDirectives(SourceRange Range,
                                          const SourceManager &SM,
                                          const LangOptions &LangOpts);
 
-/// Assuming that ``Range`` spans a const-qualified type, returns the ``const``
-/// token in ``Range`` that is responsible for const qualification. ``Range``
-/// must be valid with respect to ``SM``.  Returns ``None`` if no ``const``
+/// Assuming that ``Range`` spans a CVR-qualified type, returns the
+/// token in ``Range`` that is responsible for the qualification. ``Range``
+/// must be valid with respect to ``SM``.  Returns ``None`` if no qualifying
 /// tokens are found.
-llvm::Optional<Token> getConstQualifyingToken(CharSourceRange Range,
-                                              const ASTContext &Context,
-                                              const SourceManager &SM);
+/// \note: doesn't support member function qualifiers.
+llvm::Optional<Token> getQualifyingToken(tok::TokenKind TK,
+                                         CharSourceRange Range,
+                                         const ASTContext &Context,
+                                         const SourceManager &SM);
 
 } // namespace lexer
 } // namespace utils
index 78557c5..f801b18 100644 (file)
@@ -37,6 +37,9 @@ const T p32(T t) { return t; }
 template <typename T>
 typename std::add_const<T>::type n15(T v) { return v; }
 
+template <bool B>
+struct MyStruct {};
+
 template <typename A>
 class Klazz {
 public:
@@ -128,10 +131,46 @@ const Klazz<const int> p12() {}
 // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const int>'
 // CHECK-FIXES: Klazz<const int> p12() {}
 
+const Klazz<const Klazz<const int>> p33() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<
+// CHECK-FIXES: Klazz<const Klazz<const int>> p33() {}
+
 const Klazz<const int>* const p13() {}
 // CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const int> *
 // CHECK-FIXES: const Klazz<const int>* p13() {}
 
+const Klazz<const int>* const volatile p14() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const int> *
+// CHECK-FIXES: const Klazz<const int>* volatile p14() {}
+
+const MyStruct<0 < 1> p34() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const MyStruct<0 < 1>'
+// CHECK-FIXES: MyStruct<0 < 1> p34() {}
+
+MyStruct<0 < 1> const p35() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const MyStruct<0 < 1>'
+// CHECK-FIXES: MyStruct<0 < 1> p35() {}
+
+Klazz<MyStruct<0 < 1> const> const p36() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const MyStru
+// CHECK-FIXES: Klazz<MyStruct<0 < 1> const> p36() {}
+
+const Klazz<MyStruct<0 < 1> const> *const p37() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const MyStru
+// CHECK-FIXES: const Klazz<MyStruct<0 < 1> const> *p37() {}
+
+Klazz<const MyStruct<0 < 1>> const p38() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const MyStru
+// CHECK-FIXES: Klazz<const MyStruct<0 < 1>> p38() {}
+
+const Klazz<const MyStruct<0 < 1>> p39() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<
+// CHECK-FIXES: Klazz<const MyStruct<0 < 1>> p39() {}
+
+const Klazz<const MyStruct<(0 > 1)>> p40() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type 'const Klazz<const MyStru
+// CHECK-FIXES: Klazz<const MyStruct<(0 > 1)>> p40() {}
+
 // re-declaration of p15.
 const int p15();
 // CHECK-FIXES: int p15();