ymandel updated this revision to Diff 169290.
ymandel marked 11 inline comments as done.
ymandel added a comment.

Comment fix.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D53025

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ConstValueReturnCheck.cpp
  clang-tidy/readability/ConstValueReturnCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/utils/LexerUtils.cpp
  clang-tidy/utils/LexerUtils.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-const-value-return.rst
  test/clang-tidy/Inputs/readability-const-value-return/
  test/clang-tidy/Inputs/readability-const-value-return/macro-def.h
  test/clang-tidy/check_clang_tidy.py
  test/clang-tidy/readability-const-value-return.cpp

Index: test/clang-tidy/readability-const-value-return.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/readability-const-value-return.cpp
@@ -0,0 +1,186 @@
+// RUN: %check_clang_tidy %s readability-const-value-return %t -- -- -I %S/Inputs/readability-const-value-return
+
+#include "macro-def.h"
+
+//  p# = positive test
+//  n# = negative test
+
+const int p1() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinder compiler optimizations
+// CHECK-FIXES: int p1() {
+  return 1;
+}
+
+const int p15();
+// CHECK-FIXES: int p15();
+
+template <typename A> class Klazz {};
+class Clazz {
+ public:
+  Clazz *const p2() {
+    // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values
+    // CHECK-FIXES: Clazz *p2() {
+    return this;
+  }
+
+  Clazz *const p3();
+  // CHECK-FIXES: Clazz *p3();
+
+  const int p4() const {
+    // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values h
+    // CHECK-FIXES: int p4() const {
+    return 4;
+  }
+
+  const Klazz<const int>* const p5() const;
+  // CHECK-FIXES: const Klazz<const int>* p5() const;
+
+  const Clazz operator++(int x) {  //  p12
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz operator++(int x) {
+  }
+
+  struct Strukt {
+    int i;
+  };
+
+  const Strukt p6() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Strukt p6() {}
+
+  // No warning is emitted here, because this is only the declaration.  The
+  // warning will be associated with the definition, below.
+  const Strukt* const p7();
+  // CHECK-FIXES: const Strukt* p7();
+
+  static const int p8() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: static int p8() {}
+
+  static const Strukt p9() {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: static Strukt p9() {}
+
+  int n0() const { return 0; }
+  const Klazz<const int>& n11(const Klazz<const int>) const;
+};
+
+Clazz *const Clazz::p3() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz *Clazz::p3() {
+  return this;
+}
+
+const Klazz<const int>* const Clazz::p5() const {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Klazz<const int>* Clazz::p5() const {}
+
+const Clazz::Strukt* const Clazz::p7() {}
+// CHECK-MESSAGES: [[@LINE-1]]:22: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Clazz::Strukt* Clazz::p7() {}
+
+Clazz *const p10();
+// CHECK-FIXES: Clazz *p10();
+
+Clazz *const p10() {
+  // CHECK-MESSAGES: [[@LINE-1]]:8: warning: 'const'-qualified return values hin
+  // CHECK-FIXES: Clazz *p10() {
+  return new Clazz();
+}
+
+const Clazz bar;
+const Clazz *const p11() {
+  // CHECK-MESSAGES: [[@LINE-1]]:14: warning: 'const'-qualified return values hi
+  // CHECK-FIXES: const Clazz *p11() {
+  return &bar;
+}
+
+const Klazz<const int> p12() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: Klazz<const int> p12() {}
+
+const Klazz<const int>* const p13() {}
+// CHECK-MESSAGES: [[@LINE-1]]:25: warning: 'const'-qualified return values hind
+// CHECK-FIXES: const Klazz<const int>* p13() {}
+
+const auto p14() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hind
+// CHECK-FIXES: auto p14() {
+  const int i = 0;
+  return i;
+}
+
+// re-declaration of p15.
+const int p15();
+// CHECK-FIXES: int p15();
+
+const int p15() {
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: int p15() {
+  return 0;
+}
+
+// Exercise the lexer.
+
+const /* comment */ /* another comment*/ int p16() { return 0; }
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: /* comment */ /* another comment*/ int p16() { return 0; }
+
+/* comment */ const
+// CHECK-MESSAGES: [[@LINE-1]]:15: warning: 'const'-qualified return values hind
+// CHECK-FIXES: /* comment */
+// more
+/* another comment*/ int p17() { return 0; }
+
+// Test cases where the `const` token lexically is hidden behind some form of
+// indirection.
+
+#define CONSTINT const int
+CONSTINT p18() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type is 'const'-qualifed (possibly behind a macro), which hinders compiler optimizations
+
+#define CONST const
+CONST int p19() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type is 'const'-qualifed (poss
+
+// DEFFF is defined in header macro-def.h.  This tests that macros defined in
+// other files are handled correctly.
+DEFFF p20() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type is 'const'-qualifed (poss
+
+using ty = const int;
+ty p21() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type is 'const'-qualifed (possibly behind a type alias), which hinders compiler optimizations
+
+typedef const int ty2;
+ty2 p22() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: return type is 'const'-qualifed (poss
+
+// Declaration uses a macro, while definition doesn't.  In this case, we won't
+// fix the declaration, and will instead issue a warning.
+CONST int p23();
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: definition fixed, but couldn't fix this declaration.
+const int p23();
+// CHECK-FIXES: int p23();
+const int p23() {}
+// CHECK-MESSAGES: [[@LINE-1]]:1: warning: 'const'-qualified return values hinde
+// CHECK-FIXES: int p23() {}
+
+const int n1 = 1;
+const Clazz n2 = Clazz();
+const Clazz* n3 = new Clazz();
+Clazz *const n4 = new Clazz();
+const Clazz *const n5 = new Clazz();
+constexpr int n6 = 6;
+constexpr int n7() { return 8; }
+const int eight = 8;
+constexpr const int* n8() { return &eight; }
+Klazz<const int> n9();
+const Klazz<const int>* n10();
+const Klazz<const int>& Clazz::n11(const Klazz<const int>) const {}
+
+// Declaration only.
+const int n14();
+
+int **const * n_multiple_ptr();
+int *const & n_pointer_ref();
Index: test/clang-tidy/check_clang_tidy.py
===================================================================
--- test/clang-tidy/check_clang_tidy.py
+++ test/clang-tidy/check_clang_tidy.py
@@ -107,9 +107,9 @@
       check_messages_prefix = 'CHECK-MESSAGES' + file_check_suffix
       check_notes_prefix = 'CHECK-NOTES' + file_check_suffix
 
-      has_check_fix = check_fixes_prefix in input_text 
+      has_check_fix = check_fixes_prefix in input_text
       has_check_message = check_messages_prefix in input_text
-      has_check_note = check_notes_prefix in input_text 
+      has_check_note = check_notes_prefix in input_text
 
       if has_check_note and has_check_message:
         sys.exit('Please use either %s or %s but not both' %
@@ -130,6 +130,9 @@
     check_fixes_prefixes = ['CHECK-FIXES']
     check_messages_prefixes = ['CHECK-MESSAGES']
     check_notes_prefixes = ['CHECK-NOTES']
+    has_check_fixes = check_fixes_prefixes[0] in input_text
+    has_check_messages = check_messages_prefixes[0] in input_text
+    has_check_notes = check_notes_prefixes[0] in input_text
 
   # Remove the contents of the CHECK lines to avoid CHECKs matching on
   # themselves.  We need to keep the comments to preserve line numbers while
@@ -170,6 +173,7 @@
         '\n------------------------------------------------------------------')
 
   if has_check_fixes:
+    print('Checking Fixes')
     try:
       subprocess.check_output(
           ['FileCheck', '-input-file=' + temp_file_name, input_file_name,
Index: test/clang-tidy/Inputs/readability-const-value-return/macro-def.h
===================================================================
--- /dev/null
+++ test/clang-tidy/Inputs/readability-const-value-return/macro-def.h
@@ -0,0 +1,8 @@
+#ifndef MACRO_DEF_H
+#define MACRO_DEF_H
+
+// Used in testing the interaction between cross-file macro use and the lexer's
+// ability to "see" the const token.
+#define DEFFF const int
+
+#endif // MACRO_DEF_H
Index: docs/clang-tidy/checks/readability-const-value-return.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/readability-const-value-return.rst
@@ -0,0 +1,25 @@
+.. title:: clang-tidy - readability-const-value-return
+
+readability-const-value-return
+===================================
+
+Checks for functions with a ``const``-qualified return type and recommends
+removal of the ``const`` keyword. Such use of ``const`` is superfluous, and
+prevents valuable compiler optimizations.
+
+Examples:
+
+.. code-block:: c++
+		
+  const int foo();
+  const Clazz foo();
+  Clazz *const foo();
+
+Note that this does not apply to returning pointers or references to const
+values. These are fine:
+
+.. code-block:: c++
+
+   const int* foo();
+   const int& foo();
+   const Clazz* foo();
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -219,6 +219,7 @@
    portability-simd-intrinsics
    readability-avoid-const-params-in-decls
    readability-braces-around-statements
+   readability-const-value-return
    readability-container-size-empty
    readability-delete-null-pointer
    readability-deleted-default
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -106,6 +106,12 @@
   This check warns the uses of the deprecated member types of ``std::ios_base``
   and replaces those that have a non-deprecated equivalent.
 
+- New :doc:`readability-const-value-return
+  <clang-tidy/checks/readability-const-value-return>` check.
+
+  Checks for functions with a ``const``-qualified return type and recommends
+  removal of the ``const`` keyword.
+
 - New :doc:`readability-magic-numbers
   <clang-tidy/checks/readability-magic-numbers>` check.
 
Index: clang-tidy/utils/LexerUtils.h
===================================================================
--- clang-tidy/utils/LexerUtils.h
+++ clang-tidy/utils/LexerUtils.h
@@ -22,6 +22,11 @@
 Token getPreviousToken(SourceLocation Location, const SourceManager &SM,
                        const LangOptions &LangOpts, bool SkipComments = true);
 
+// Gets the start locations of all ``const`` tokens in the Range.
+std::vector<SourceLocation> getConstTokLocations(
+    CharSourceRange Range, const clang::ASTContext &Context,
+    const SourceManager &SM);
+
 } // namespace lexer
 } // namespace utils
 } // namespace tidy
Index: clang-tidy/utils/LexerUtils.cpp
===================================================================
--- clang-tidy/utils/LexerUtils.cpp
+++ clang-tidy/utils/LexerUtils.cpp
@@ -31,6 +31,31 @@
   return Token;
 }
 
+std::vector<SourceLocation> getConstTokLocations(
+    CharSourceRange Range, const clang::ASTContext &Context,
+    const SourceManager &SM) {
+  std::pair<FileID, unsigned> LocInfo =
+      SM.getDecomposedLoc(Range.getBegin());
+  StringRef File = SM.getBufferData(LocInfo.first);
+  const char *TokenBegin = File.data() + LocInfo.second;
+  Lexer RawLexer(SM.getLocForStartOfFile(LocInfo.first), Context.getLangOpts(),
+                 File.begin(), TokenBegin, File.end());
+  Token Tok;
+  std::vector<SourceLocation> Locations;
+  while (!RawLexer.LexFromRawLexer(Tok) &&
+         !SM.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation())) {
+    if (Tok.is(tok::raw_identifier)) {
+      IdentifierInfo &Info = Context.Idents.get(
+          StringRef(SM.getCharacterData(Tok.getLocation()), Tok.getLength()));
+      Tok.setIdentifierInfo(&Info);
+      Tok.setKind(Info.getTokenID());
+    }
+    if (Tok.is(tok::kw_const))
+      Locations.push_back(Tok.getLocation());
+  }
+  return Locations;
+}
+
 } // namespace lexer
 } // namespace utils
 } // namespace tidy
Index: clang-tidy/readability/ReadabilityTidyModule.cpp
===================================================================
--- clang-tidy/readability/ReadabilityTidyModule.cpp
+++ clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -12,6 +12,7 @@
 #include "../ClangTidyModuleRegistry.h"
 #include "AvoidConstParamsInDecls.h"
 #include "BracesAroundStatementsCheck.h"
+#include "ConstValueReturnCheck.h"
 #include "ContainerSizeEmptyCheck.h"
 #include "DeleteNullPointerCheck.h"
 #include "DeletedDefaultCheck.h"
@@ -50,6 +51,8 @@
         "readability-avoid-const-params-in-decls");
     CheckFactories.registerCheck<BracesAroundStatementsCheck>(
         "readability-braces-around-statements");
+    CheckFactories.registerCheck<ConstValueReturnCheck>(
+        "readability-const-value-return");
     CheckFactories.registerCheck<ContainerSizeEmptyCheck>(
         "readability-container-size-empty");
     CheckFactories.registerCheck<DeleteNullPointerCheck>(
Index: clang-tidy/readability/ConstValueReturnCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/readability/ConstValueReturnCheck.h
@@ -0,0 +1,35 @@
+//===--- ConstValueReturnCheck.h - clang-tidy -------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONSTVALUERETURNCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONSTVALUERETURNCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// Suggests removal of the `const` qualifier from any function that returns a
+/// const type.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability-const-value-return.html
+class ConstValueReturnCheck : public ClangTidyCheck {
+ public:
+  using ClangTidyCheck::ClangTidyCheck;
+  void registerMatchers(ast_matchers::MatchFinder* finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult& result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONSTVALUERETURNCHECK_H
Index: clang-tidy/readability/ConstValueReturnCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/readability/ConstValueReturnCheck.cpp
@@ -0,0 +1,107 @@
+//===--- ConstValueReturnCheck.cpp - clang-tidy ---------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ConstValueReturnCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/Optional.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+// Finds the location of the relevant const token in `Def`.
+static llvm::Optional<SourceLocation> findConstToRemove(
+    const FunctionDecl *Def, const MatchFinder::MatchResult &Result) {
+  // Since either of the locs can be in a macro, use makeFileCharRange to be
+  // sure that we have a consistent char range, located entirely in the source
+  // file.
+  CharSourceRange FileRange = Lexer::makeFileCharRange(
+      CharSourceRange::getCharRange(Def->getBeginLoc(),
+                                    Def->getNameInfo().getLoc()),
+      *Result.SourceManager, Result.Context->getLangOpts());
+
+  if (FileRange.isInvalid())
+    return None;
+
+  std::vector<SourceLocation> Locs = utils::lexer::getConstTokLocations(
+      FileRange, *Result.Context, *Result.SourceManager);
+  if (Locs.empty())
+    return None;
+
+  // If the return type is a pointer, then remove const after  asterisk, which
+  // will be the last const before function name; else, get first.
+  return Def->getReturnType()->isPointerType() ? Locs.back() : Locs.front();
+}
+
+void ConstValueReturnCheck::registerMatchers(MatchFinder *Finder) {
+  // Find all the functions for which the return types are const qualified.
+  Finder->addMatcher(
+      functionDecl(returns(isConstQualified()), isDefinition()).bind("func"),
+      this);
+}
+
+void ConstValueReturnCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Def = Result.Nodes.getNodeAs<FunctionDecl>("func");
+  if (!Def->getReturnType().isLocalConstQualified()) {
+    // From the matcher, we know this is const qualified.  But it is not done
+    // locally, so we can't offer a fix -- just a warning.
+    diag(Def->getInnerLocStart(),
+         "return type is 'const'-qualifed (possibly behind a type alias), "
+         "which hinders compiler optimizations");
+    return;
+  }
+
+  llvm::Optional<SourceLocation> Loc = findConstToRemove(Def, Result);
+  if (!Loc) {
+    // We couldn't find the const token, most likely because it is hidden behind
+    // a macro. So, warn the user, but offer no fix.
+    diag(Def->getInnerLocStart(),
+         "return type is 'const'-qualifed (possibly behind a macro), which "
+         "hinders compiler optimizations");
+    return;
+  }
+
+  // Limit the scope of Diagnostics object below so that we can emit further
+  // diagnostics after the scope. Clang only supports one in-flight diagnostic
+  // at a time.
+  std::vector<std::pair<clang::SourceLocation, std::string>> UnfixabledDecls;
+  {
+    // Fix the definition and any visible declarations, but don't warn
+    // seperately for each declaration -- associate all fixes with the single
+    // warning at the definition.
+    DiagnosticBuilder Diagnostics =
+        diag(*Loc,
+             "'const'-qualified return values hinder compiler optimizations")
+        << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(*Loc, *Loc));
+
+    for (const FunctionDecl *Decl = Def->getPreviousDecl(); Decl != nullptr;
+         Decl = Decl->getPreviousDecl()) {
+      if (llvm::Optional<SourceLocation> PrevLoc =
+              findConstToRemove(Decl, Result)) {
+        Diagnostics << FixItHint::CreateRemoval(
+            CharSourceRange::getTokenRange(*PrevLoc, *PrevLoc));
+      } else {
+        UnfixabledDecls.emplace_back(
+            Decl->getInnerLocStart(),
+            "definition fixed, but couldn't fix this declaration.");
+      }
+    }
+  }
+  for (auto &d : UnfixabledDecls)
+    diag(d.first, d.second);
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/readability/CMakeLists.txt
===================================================================
--- clang-tidy/readability/CMakeLists.txt
+++ clang-tidy/readability/CMakeLists.txt
@@ -3,6 +3,7 @@
 add_clang_library(clangTidyReadabilityModule
   AvoidConstParamsInDecls.cpp
   BracesAroundStatementsCheck.cpp
+  ConstValueReturnCheck.cpp
   ContainerSizeEmptyCheck.cpp
   DeleteNullPointerCheck.cpp
   DeletedDefaultCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to