Index: clang-tidy/readability/CMakeLists.txt =================================================================== --- clang-tidy/readability/CMakeLists.txt +++ clang-tidy/readability/CMakeLists.txt @@ -29,6 +29,7 @@ StaticDefinitionInAnonymousNamespaceCheck.cpp StringCompareCheck.cpp UniqueptrDeleteReleaseCheck.cpp + UnmodifiedNonConstVariableCheck.cpp LINK_LIBS clangAST Index: clang-tidy/readability/ReadabilityTidyModule.cpp =================================================================== --- clang-tidy/readability/ReadabilityTidyModule.cpp +++ clang-tidy/readability/ReadabilityTidyModule.cpp @@ -36,6 +36,7 @@ #include "StaticDefinitionInAnonymousNamespaceCheck.h" #include "StringCompareCheck.h" #include "UniqueptrDeleteReleaseCheck.h" +#include "UnmodifiedNonConstVariableCheck.h" namespace clang { namespace tidy { @@ -78,6 +79,8 @@ "readability-static-definition-in-anonymous-namespace"); CheckFactories.registerCheck( "readability-string-compare"); + CheckFactories.registerCheck( + "readability-unmodified-non-const-variable"); CheckFactories.registerCheck( "readability-named-parameter"); CheckFactories.registerCheck( Index: clang-tidy/readability/UnmodifiedNonConstVariableCheck.h =================================================================== --- /dev/null +++ clang-tidy/readability/UnmodifiedNonConstVariableCheck.h @@ -0,0 +1,47 @@ +//===--- UnmodifiedNonConstVariableCheck.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_UNMODIFIEDNONCONSTVARIABLECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UNMODIFIEDNONCONSTVARIABLECHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace readability { + +/// Finds declarations of non-const variables that never get modified. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability-unmodified-non-const-variable.html +class UnmodifiedNonConstVariableCheck : public ClangTidyCheck { +public: + UnmodifiedNonConstVariableCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + MaxPerTranslationUnit(Options.get("MaxPerTranslationUnit", 50)), + IgnorePointers(Options.get("IgnorePointers", false)) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void onStartOfTranslationUnit() override { Count = 0; } + void storeOptions(ClangTidyOptions::OptionMap &Opts) override { + Options.store(Opts, "MaxPerTranslationUnit", MaxPerTranslationUnit); + Options.store(Opts, "IgnorePointers", IgnorePointers); + } + +private: + const int MaxPerTranslationUnit; + const bool IgnorePointers; + int Count; +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_UNMODIFIEDNONCONSTVARIABLECHECK_H Index: clang-tidy/readability/UnmodifiedNonConstVariableCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/readability/UnmodifiedNonConstVariableCheck.cpp @@ -0,0 +1,60 @@ +//===--- UnmodifiedNonConstVariableCheck.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 "UnmodifiedNonConstVariableCheck.h" +#include "../utils/ASTUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace readability { + +void UnmodifiedNonConstVariableCheck::registerMatchers(MatchFinder *Finder) { + const auto ConstTypes = qualType( + anyOf(isConstQualified(), referenceType(pointee(isConstQualified())))); + const auto TypeFilter = hasType( + qualType(unless(anyOf(substTemplateTypeParmType(), + hasDescendant(substTemplateTypeParmType()))), + !IgnorePointers ? ConstTypes + : qualType(anyOf(ConstTypes, pointerType())))); + const auto DeclFilter = + anyOf(parmVarDecl(), isImplicit(), isConstexpr(), TypeFilter); + Finder->addMatcher( + varDecl(unless(DeclFilter), hasAncestor(compoundStmt().bind("stmt"))) + .bind("decl"), + this); +} + +void UnmodifiedNonConstVariableCheck::check( + const MatchFinder::MatchResult &Result) { + if (++Count > MaxPerTranslationUnit) + return; + + const auto *Decl = Result.Nodes.getNodeAs("decl"); + if (Decl->Decl::getLocStart().isMacroID()) + return; + const auto *Compound = Result.Nodes.getNodeAs("stmt"); + const auto Exprs = + match(findAll(declRefExpr(to(varDecl(equalsNode(Decl)))).bind("expr")), + *Compound, *Result.Context); + for (const auto &Node : Exprs) { + if (utils::isModified(*Node.getNodeAs("expr"), *Compound, + Result.Context)) + return; + } + diag(Decl->getLocation(), + "declaring a non-const variable but never modified it"); +} + +} // namespace readability +} // namespace tidy +} // namespace clang Index: clang-tidy/utils/ASTUtils.h =================================================================== --- clang-tidy/utils/ASTUtils.h +++ clang-tidy/utils/ASTUtils.h @@ -27,6 +27,10 @@ bool exprHasBitFlagWithSpelling(const Expr *Flags, const SourceManager &SM, const LangOptions &LangOpts, StringRef FlagName); + +// Checks whether `Exp` is (potentially) modified within `Stm`. +bool isModified(const Expr& Exp, const Stmt& Stm, ASTContext* Context); + } // namespace utils } // namespace tidy } // namespace clang Index: clang-tidy/utils/ASTUtils.cpp =================================================================== --- clang-tidy/utils/ASTUtils.cpp +++ clang-tidy/utils/ASTUtils.cpp @@ -67,6 +67,116 @@ return true; } +namespace { +class MatchCallbackAdaptor : public MatchFinder::MatchCallback { +public: + explicit MatchCallbackAdaptor( + std::function Func) + : Func(std::move(Func)) {} + void run(const MatchFinder::MatchResult &Result) override { Func(Result); } + +private: + std::function Func; +}; +} // namespace + +bool isModified(const Expr &Exp, const Stmt &Stm, ASTContext *Context) { + // LHS of any assignment operators. + const auto AsAssignmentLhs = + binaryOperator(isAssignmentOperator(), hasLHS(equalsNode(&Exp))); + + // Operand of increment/decrement operators. + const auto AsIncDecOperand = + unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")), + hasUnaryOperand(equalsNode(&Exp))); + + // Invoking non-const member function. + const auto NonConstMethod = cxxMethodDecl(unless(isConst())); + const auto AsNonConstThis = expr( + anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(&Exp))), + cxxOperatorCallExpr(callee(NonConstMethod), + hasArgument(0, equalsNode(&Exp))))); + + // Used as non-const-ref argument when calling a function. + const auto NonConstRefType = + referenceType(pointee(unless(isConstQualified()))); + const auto NonConstRefParam = forEachArgumentWithParam( + equalsNode(&Exp), parmVarDecl(hasType(qualType(NonConstRefType)))); + const auto AsNonConstRefArg = + anyOf(callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam)); + + // Returned as non-const-ref. + // If we're returning 'Exp' directly then it's returned as non-const-ref. + // Otherwise there will be an ImplicitCastExpr in between. + const auto AsNonConstRefReturn = returnStmt(hasReturnValue(equalsNode(&Exp))); + + // Taking address of 'Exp'. + // In theory we can follow the pointer and see whether the pointer itself + // escaped 'Stm', or is dereferenced and the dereferencing expression is + // modified. This is left for future improvements. + const auto AsAmpersandOperand = + unaryOperator(hasOperatorName("&"), + unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))), + hasUnaryOperand(equalsNode(&Exp))); + const auto AsPointerFromArrayDecay = + castExpr(hasCastKind(CK_ArrayToPointerDecay), + unless(hasParent(arraySubscriptExpr())), has(equalsNode(&Exp))); + + // Check whether 'Exp' is directly modified as a whole. + MatchFinder Finder; + bool HasMatch = false; + MatchCallbackAdaptor Callback( + [&HasMatch](const MatchFinder::MatchResult &) { HasMatch = true; }); + Finder.addMatcher( + findAll(expr(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis, + AsNonConstRefArg, AsAmpersandOperand, + AsPointerFromArrayDecay))), + &Callback); + Finder.addMatcher(findAll(AsNonConstRefReturn), &Callback); + Finder.match(Stm, *Context); + if (HasMatch) + return true; + + // Check whether any member of 'Exp' is modified. + const auto MemberExprs = match( + findAll(memberExpr(hasObjectExpression(equalsNode(&Exp))).bind("expr")), + Stm, *Context); + for (const auto &Node : MemberExprs) { + if (isModified(*Node.getNodeAs("expr"), Stm, Context)) + return true; + } + + // In the case of 'Exp' being an array, check whether any element is modified. + const auto SubscriptExprs = match( + findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(&Exp)))) + .bind("expr")), + Stm, *Context); + for (const auto &Node : SubscriptExprs) { + if (isModified(*Node.getNodeAs("expr"), Stm, Context)) + return true; + } + + // If 'Exp' is bound to a non-const reference, check all declRefExpr to that. + const auto Decls = match( + stmt(forEachDescendant( + varDecl(hasType(referenceType(pointee(unless(isConstQualified())))), + hasInitializer(equalsNode(&Exp))) + .bind("decl"))), + Stm, *Context); + for (const auto &DeclNode : Decls) { + const auto Exprs = match( + findAll(declRefExpr(to(equalsNode(DeclNode.getNodeAs("decl")))) + .bind("expr")), + Stm, *Context); + for (const auto &ExprNode : Exprs) { + if (isModified(*ExprNode.getNodeAs("expr"), Stm, Context)) + return true; + } + } + + return false; +} + } // namespace utils } // namespace tidy } // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -57,6 +57,11 @@ Improvements to clang-tidy -------------------------- +- New :doc:`readability-unmodified-non-const-variable + ` check + + Finds declarations of non-const variables that never get modified. + - New module `abseil` for checks related to the `Abseil `_ library. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -91,8 +91,8 @@ cppcoreguidelines-pro-type-vararg cppcoreguidelines-slicing cppcoreguidelines-special-member-functions - fuchsia-header-anon-namespaces (redirects to google-build-namespaces) fuchsia-default-arguments + fuchsia-header-anon-namespaces (redirects to google-build-namespaces) fuchsia-multiple-inheritance fuchsia-overloaded-operator fuchsia-statically-constructed-objects @@ -229,4 +229,5 @@ readability-static-definition-in-anonymous-namespace readability-string-compare readability-uniqueptr-delete-release + readability-unmodified-non-const-variable zircon-temporary-objects Index: docs/clang-tidy/checks/readability-unmodified-non-const-variable.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/readability-unmodified-non-const-variable.rst @@ -0,0 +1,15 @@ +.. title:: clang-tidy - readability-unmodified-non-const-variable + +readability-unmodified-non-const-variable +========================================= + +Finds declarations of non-const variables that never get modified. + +For example: + +.. code-block:: c++ + + int simple() { + int x = 10; // x is declared as non-const but is never modified. + return x + 1; + } Index: test/clang-tidy/readability-unmodified-non-const-variable.cpp =================================================================== --- /dev/null +++ test/clang-tidy/readability-unmodified-non-const-variable.cpp @@ -0,0 +1,459 @@ +// RUN: %check_clang_tidy %s readability-unmodified-non-const-variable %t \ +// RUN: -config="{CheckOptions: \ +// RUN: [{key: readability-unmodified-non-const-variable.MaxPerTranslationUnit,\ +// RUN: value: '100000'}]}" -- + +template +struct pair { + T1 first; + T2 second; + + void set(T1 f, T2 s); +}; + +void touch(int&); +void touch(int*); + +int simple() { + int x = 10; + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it [readability-unmodified-non-const-variable] + return x + 1; +} + +void modified1() { + int x = 10; + touch(x); +} + +void modified2() { + int x = 10; + touch(&x); +} + +int modified3() { + int x = 10; + x += 20; + return x; +} + +int callNonConstMember() { + pair p1; + p1.set(10, 20); + return p1.first + p1.second; +} + +int followNonConstReference() { + pair p1; + auto& p2 = p1; + p2.first = 10; + return p1.first; +} + +bool global; +char np_global = 0; // globals can't be known to be const + +namespace foo { +int scoped; +float np_scoped = 1; // namespace variables are like globals +} // namespace foo + +void some_function(double, wchar_t); + +void some_function(double np_arg0, wchar_t np_arg1) { + int p_local0 = 2; + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it + + const int np_local1 = 42; + + unsigned int np_local2 = 3; + np_local2 <<= 4; + + int np_local3 = 4; + ++np_local3; + int np_local4 = 4; + np_local4++; + + int np_local5 = 4; + --np_local5; + int np_local6 = 4; + np_local6--; +} + +void some_lambda_environment_capture_all_by_reference(double np_arg0) { + int np_local0 = 0; + int p_local0 = 1; + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it + + const int np_local3 = 2; + + // Capturing all variables by reference prohibits making them const. + [&]() { ++np_local0; }; + + int p_local1 = 0; + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it +} + +void some_lambda_environment_capture_all_by_value(double np_arg0) { + int np_local0 = 0; + int p_local0 = 1; + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it + + const int np_local2 = 2; + + // Capturing by value has no influence on them. + [=]() { (void)p_local0; }; + + np_local0 += 10; +} + +void function_inout_pointer(int *inout); +void function_in_pointer(const int *in); + +void some_pointer_taking(int *out) { + int np_local0 = 42; + const int *const p0_np_local0 = &np_local0; + int *const p1_np_local0 = &np_local0; + + int np_local1 = 42; + function_inout_pointer(&np_local1); + + // Prevents const. + int np_local2 = 42; + out = &np_local2; // This returns and invalid address, its just about the AST + + int p_local0 = 42; + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it + const int *const p0_p_local0 = &p_local0; + + int p_local1 = 42; + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it + function_in_pointer(&p_local1); +} + +void function_inout_ref(int &inout); +void function_in_ref(const int &in); + +void some_reference_taking() { + int np_local0 = 42; + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it + const int &r0_np_local0 = np_local0; + int &r1_np_local0 = np_local0; + // CHECK-MESSAGES: [[@LINE-1]]:8: warning: declaring a non-const variable but never modified it + const int &r2_np_local0 = r1_np_local0; + + int np_local1 = 42; + function_inout_ref(np_local1); + + int p_local0 = 42; + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it + const int &r0_p_local0 = p_local0; + + int p_local1 = 42; + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it + function_in_ref(p_local1); +} + +double *non_const_pointer_return() { + double p_local0 = 0.0; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + double np_local0 = 24.4; + + return &np_local0; +} + +const double *const_pointer_return() { + double p_local0 = 0.0; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + double p_local1 = 24.4; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + return &p_local1; +} + +double &non_const_ref_return() { + double p_local0 = 0.0; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + double np_local0 = 42.42; + return np_local0; +} + +const double &const_ref_return() { + double p_local0 = 0.0; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + double p_local1 = 24.4; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + return p_local1; +} + +double *&return_non_const_pointer_ref() { + double *np_local0 = nullptr; + return np_local0; +} + +void overloaded_arguments(const int &in); +void overloaded_arguments(int &inout); +void overloaded_arguments(const int *in); +void overloaded_arguments(int *inout); + +void function_calling() { + int np_local0 = 42; + overloaded_arguments(np_local0); + + const int np_local1 = 42; + overloaded_arguments(np_local1); + + int np_local2 = 42; + overloaded_arguments(&np_local2); + + const int np_local3 = 42; + overloaded_arguments(&np_local3); +} + +template +void define_locals(T np_arg0, T &np_arg1, int np_arg2) { + T np_local0 = 0; + np_local0 += np_arg0 * np_arg1; + + T np_local1 = 42; + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: declaring a non-const variable but never modified it + np_local0 += np_local1; + + // Used as argument to an overloaded function with const and non-const. + T np_local2 = 42; + // CHECK-MESSAGES: [[@LINE-1]]:5: warning: declaring a non-const variable but never modified it + overloaded_arguments(np_local2); + + int np_local4 = 42; + // non-template values are ok still. + int p_local0 = 42; + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: declaring a non-const variable but never modified it + np_local4 += p_local0; +} + +void template_instantiation() { + const int np_local0 = 42; + int np_local1 = 42; + + define_locals(np_local0, np_local1, np_local0); + define_locals(np_local1, np_local1, np_local1); +} + +struct ConstNonConstClass { + ConstNonConstClass(); + ConstNonConstClass(double &np_local0); + double nonConstMethod() {} + double constMethod() const {} + double modifyingMethod(double &np_arg0) const; + + double NonConstMember; + const double ConstMember; + + double &NonConstMemberRef; + const double &ConstMemberRef; + + double *NonConstMemberPtr; + const double *ConstMemberPtr; +}; + +void direct_class_access() { + ConstNonConstClass np_local0; + + np_local0.constMethod(); + np_local0.nonConstMethod(); + + ConstNonConstClass p_local0; + // CHECK-MESSAGES: [[@LINE-1]]:22: warning: declaring a non-const variable but never modified it + p_local0.constMethod(); + + ConstNonConstClass p_local1; + // CHECK-MESSAGES: [[@LINE-1]]:22: warning: declaring a non-const variable but never modified it + double np_local1; + p_local1.modifyingMethod(np_local1); + + double np_local2; + ConstNonConstClass p_local2(np_local2); + // CHECK-MESSAGES: [[@LINE-1]]:22: warning: declaring a non-const variable but never modified it + + ConstNonConstClass np_local3; + np_local3.NonConstMember = 42.; + + ConstNonConstClass np_local4; + np_local4.NonConstMemberRef = 42.; + + ConstNonConstClass np_local5; + // CHECK-MESSAGES: [[@LINE-1]]:22: warning: declaring a non-const variable but never modified it + *np_local5.NonConstMemberPtr = 42.; + + ConstNonConstClass p_local3; + // CHECK-MESSAGES: [[@LINE-1]]:22: warning: declaring a non-const variable but never modified it + const double val0 = p_local3.NonConstMember; + const double val1 = p_local3.NonConstMemberRef; + const double val2 = *p_local3.NonConstMemberPtr; +} + +struct OperatorsAsConstAsPossible { + OperatorsAsConstAsPossible &operator+=(const OperatorsAsConstAsPossible &rhs); + OperatorsAsConstAsPossible operator+(const OperatorsAsConstAsPossible &rhs) const; +}; + +struct NonConstOperators { +}; +NonConstOperators operator+(NonConstOperators &lhs, NonConstOperators &rhs); +NonConstOperators operator-(NonConstOperators lhs, NonConstOperators rhs); + +void internal_operator_calls() { + OperatorsAsConstAsPossible np_local0; + OperatorsAsConstAsPossible np_local1; + OperatorsAsConstAsPossible p_local0; + // CHECK-MESSAGES: [[@LINE-1]]:30: warning: declaring a non-const variable but never modified it + OperatorsAsConstAsPossible p_local1; + // CHECK-MESSAGES: [[@LINE-1]]:30: warning: declaring a non-const variable but never modified it + + np_local0 += p_local0; + np_local1 = p_local0 + p_local1; + + NonConstOperators np_local2; + NonConstOperators np_local3; + NonConstOperators np_local4; + + np_local2 = np_local3 + np_local4; + + NonConstOperators p_local2; + // CHECK-MESSAGES: [[@LINE-1]]:21: warning: declaring a non-const variable but never modified it + NonConstOperators p_local3 = p_local2 - p_local2; + // CHECK-MESSAGES: [[@LINE-1]]:21: warning: declaring a non-const variable but never modified it +} + +struct MyVector { + double *begin(); + const double *begin() const; + + double *end(); + const double *end() const; + + double &operator[](int index); + double operator[](int index) const; + + double values[100]; +}; + +void vector_usage() { + double np_local0[10]; + np_local0[5] = 42.; + + MyVector np_local1; + np_local1[5] = 42.; + + double p_local0[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + double p_local1 = p_local0[5]; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + + // The following subscript calls suprisingly choose the non-const operator + // version. + MyVector np_local2; + double p_local2 = np_local2[42]; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + + MyVector np_local3; + const double np_local4 = np_local3[42]; + + // This subscript results in const overloaded operator. + const MyVector np_local5{}; + double p_local3 = np_local5[42]; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it +} + +void const_handle(const double &np_local0); +void const_handle(const double *np_local0); + +void non_const_handle(double &np_local0); +void non_const_handle(double *np_local0); + +void handle_from_array() { + // Non-const handle from non-const array forbids declaring the array as const + double np_local0[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + double *p_local0 = &np_local0[1]; + // CHECK-MESSAGES: [[@LINE-1]]:11: warning: declaring a non-const variable but never modified it + + double np_local1[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + double &non_const_ref = np_local1[1]; + // CHECK-MESSAGES: [[@LINE-1]]:11: warning: declaring a non-const variable but never modified it + + double np_local2[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + double *np_local3; + np_local3 = &np_local2[5]; + + double np_local4[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + non_const_handle(np_local4[2]); + double np_local5[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + non_const_handle(&np_local5[2]); + + // Constant handles are ok + double p_local1[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + const double *p_local2 = &p_local1[2]; + // CHECK-MESSAGES: [[@LINE-1]]:17: warning: declaring a non-const variable but never modified it + + double p_local3[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + const double &const_ref = p_local3[2]; + + double p_local4[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + const double *const_ptr; + const_ptr = &p_local4[2]; + + double p_local5[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + const_handle(p_local5[2]); + double p_local6[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.}; + // CHECK-MESSAGES: [[@LINE-1]]:10: warning: declaring a non-const variable but never modified it + const_handle(&p_local6[2]); +} + +void range_for() { + int np_local0[2] = {1, 2}; + for (int &non_const_ref : np_local0) { + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: declaring a non-const variable but never modified it + } + + int np_local1[2] = {1, 2}; + for (auto &non_const_ref : np_local1) { + // CHECK-MESSAGES: [[@LINE-1]]:14: warning: declaring a non-const variable but never modified it + } + + int np_local2[2] = {1, 2}; + for (auto &&non_const_ref : np_local2) { + // CHECK-MESSAGES: [[@LINE-1]]:15: warning: declaring a non-const variable but never modified it + } + + int *np_local3[2] = {&np_local0[0], &np_local0[1]}; + for (int *non_const_ptr : np_local3) { + // CHECK-MESSAGES: [[@LINE-1]]:13: warning: declaring a non-const variable but never modified it + } + + int *np_local4[2] = {&np_local0[0], &np_local0[1]}; + for (auto *non_const_ptr : np_local4) { + // CHECK-MESSAGES: [[@LINE-1]]:14: warning: declaring a non-const variable but never modified it + } + + int p_local0[2] = {1, 2}; + for (int value : p_local0) { + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: declaring a non-const variable but never modified it + } + + int p_local1[2] = {1, 2}; + for (const int &const_ref : p_local1) { + } + + int *p_local2[2] = {&np_local0[0], &np_local0[1]}; + for (const int *con_ptr : p_local2) { + // CHECK-MESSAGES: [[@LINE-1]]:19: warning: declaring a non-const variable but never modified it + } + + int *p_local3[2] = {nullptr, nullptr}; + for (const auto *con_ptr : p_local3) { + // CHECK-MESSAGES: [[@LINE-1]]:20: warning: declaring a non-const variable but never modified it + } +}