[PATCH] D45444: [clang-tidy] implement new check for const-correctness

2018-08-07 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.



> - there seems to be a false positive with array-to-pointer decay. 
> ExprMutAnalyzer does think of it, but maybe there is a bug in it.

Could you give a concrete example of this?


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45444



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50447: [clang-tidy] Add a whitelistClasses option in performance-for-range-copy check.

2018-08-08 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D50447#1192393, @JonasToth wrote:

> ... just check if the variable is dereferenced in the scope of the loop (any
>  declRefExpr exists).


+1
And I would imagine it's very rare (as in categories not raw number of 
occurrences) for a loop variable to be not used in the loop body so it's 
probably a safe change. Though I'm interested to learn whether there's any real 
false negative by doing this.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50447



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45444: [clang-tidy] implement new check for const-correctness

2018-08-10 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D45444#1191874, @JonasToth wrote:

> > Could you give a concrete example of this?
>
> vi llvm/lib/Demangle/ItaniumDemangle.cpp +1762
>
> /home/jonas/opt/llvm/lib/Demangle/ItaniumDemangle.cpp:1762:7: warning:
>  variable 'num' of type 'char [FloatData::max_demangled_size]' can
>  be declared 'const' [cppcoreguidelines-const-correctness]
>    char num[FloatData::max_demangled_size] = {0};
>   ^
>  /home/jonas/opt/llvm/lib/Demangle/ItaniumDemangle.cpp:1763:7: warning:
>  variable 'n' of type 'int' can be declared 'const'
>  [cppcoreguidelines-const-correctness]
>        int n = snprintf(num, sizeof(num), FloatData::spec, value);
>   ^


Looks like related to template.
the exact type of `num` depends on `Float`
and the `CallExpr` is not even resolved because we don't know the type of the 
arguments yet (adl, overload resolution, etc.)
So the AST doesn't contain any array to pointer decay.

I guess we should suppress the check in such cases.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45444



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50447: [clang-tidy] Omit cases where loop variable is not used in loop body in performance-for-range-copy.

2018-08-10 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D50447#1194967, @JonasToth wrote:

> Do you think it is a bad idea? If the variable is not used it is ok to
>  ignore it in this particular circumstance. Other warnings/check should
>  deal with such a situation IMHO.
>
> Am 10.08.2018 um 10:29 schrieb Roman Lebedev via Phabricator:
>
> > lebedev.ri added a comment.
> > 
> > It seems this ended up silently being a catch-all, with no option to 
> > control this behavior, and i don't see any comments discussing this..
> > 
> > Repository:
> > 
> >   rL LLVM
> > 
> > https://reviews.llvm.org/D50447


Not sure whether hokein have done that already but I did run through our code 
base and AFAICT there's no false negative.


Repository:
  rL LLVM

https://reviews.llvm.org/D50447



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45444: [clang-tidy] implement new check for const-correctness

2018-08-11 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D45444#1196271, @JonasToth wrote:

> Always the same with the templates ;) So uninstantiated templates should
>  just be ignored.
>
> I think it would be better to have it in the ExprMutAnalyzer, because
>  that part can not decide on const-ness. Fixing it here would just
>  circumvent the problem but not fix it, would you agree?


Agreed :)
I'll create a diff handling various template related cases in 
ExprMutationAnalyzer.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45444



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50605: [ASTMatchers] Let hasAnyArgument also support CXXUnresolvedConstructExpr

2018-08-11 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
Herald added a subscriber: cfe-commits.

Repository:
  rC Clang

https://reviews.llvm.org/D50605

Files:
  include/clang/AST/ExprCXX.h
  include/clang/ASTMatchers/ASTMatchers.h
  unittests/ASTMatchers/ASTMatchersTraversalTest.cpp


Index: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -406,16 +406,35 @@
   auto HasArgumentY = hasAnyArgument(
   ignoringParenImpCasts(declRefExpr(to(varDecl(hasName("y"));
   StatementMatcher CallArgumentY = callExpr(HasArgumentY);
+  StatementMatcher CtorArgumentY = cxxConstructExpr(HasArgumentY);
+  StatementMatcher UnresolvedCtorArgumentY =
+  cxxUnresolvedConstructExpr(HasArgumentY);
   StatementMatcher ObjCCallArgumentY = objcMessageExpr(HasArgumentY);
   EXPECT_TRUE(matches("void x(int, int) { int y; x(1, y); }", CallArgumentY));
   EXPECT_TRUE(matches("void x(int, int) { int y; x(y, 42); }", CallArgumentY));
+  EXPECT_TRUE(matches("struct Y { Y(int, int); };"
+  "void x() { int y; (void)Y(1, y); }",
+  CtorArgumentY));
+  EXPECT_TRUE(matches("struct Y { Y(int, int); };"
+  "void x() { int y; (void)Y(y, 42); }",
+  CtorArgumentY));
+  EXPECT_TRUE(matches("template  void x() { int y; (void)Y(1, y); }",
+  UnresolvedCtorArgumentY));
+  EXPECT_TRUE(matches("template  void x() { int y; (void)Y(y, 42); }",
+  UnresolvedCtorArgumentY));
   EXPECT_TRUE(matchesObjC("@interface I -(void)f:(int) y; @end "
   "void x(I* i) { int y; [i f:y]; }",
   ObjCCallArgumentY));
   EXPECT_FALSE(matchesObjC("@interface I -(void)f:(int) z; @end "
"void x(I* i) { int z; [i f:z]; }",
ObjCCallArgumentY));
   EXPECT_TRUE(notMatches("void x(int, int) { x(1, 2); }", CallArgumentY));
+  EXPECT_TRUE(notMatches("struct Y { Y(int, int); };"
+ "void x() { int y; (void)Y(1, 2); }",
+ CtorArgumentY));
+  EXPECT_TRUE(notMatches("template "
+ "void x() { int y; (void)Y(1, 2); }",
+ UnresolvedCtorArgumentY));
 
   StatementMatcher ImplicitCastedArgument = callExpr(
 hasAnyArgument(implicitCastExpr()));
Index: include/clang/ASTMatchers/ASTMatchers.h
===
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -3537,9 +3537,9 @@
 /// objcMessageExpr(hasAnyArgument(integerLiteral(equals(12
 ///   matches [i f:12]
 AST_POLYMORPHIC_MATCHER_P(hasAnyArgument,
-  AST_POLYMORPHIC_SUPPORTED_TYPES(CallExpr,
-  CXXConstructExpr,
-  ObjCMessageExpr),
+  AST_POLYMORPHIC_SUPPORTED_TYPES(
+  CallExpr, CXXConstructExpr,
+  CXXUnresolvedConstructExpr, ObjCMessageExpr),
   internal::Matcher, InnerMatcher) {
   for (const Expr *Arg : Node.arguments()) {
 BoundNodesTreeBuilder Result(*Builder);
Index: include/clang/AST/ExprCXX.h
===
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -3426,16 +3426,22 @@
   unsigned arg_size() const { return NumArgs; }
 
   using arg_iterator = Expr **;
+  using arg_range = llvm::iterator_range;
 
   arg_iterator arg_begin() { return getTrailingObjects(); }
   arg_iterator arg_end() { return arg_begin() + NumArgs; }
+  arg_range arguments() { return arg_range(arg_begin(), arg_end()); }
 
   using const_arg_iterator = const Expr* const *;
+  using arg_const_range = llvm::iterator_range;
 
   const_arg_iterator arg_begin() const { return getTrailingObjects(); }
   const_arg_iterator arg_end() const {
 return arg_begin() + NumArgs;
   }
+  arg_const_range arguments() const {
+return arg_const_range(arg_begin(), arg_end());
+  }
 
   Expr *getArg(unsigned I) {
 assert(I < NumArgs && "Argument index out-of-range");


Index: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -406,16 +406,35 @@
   auto HasArgumentY = hasAnyArgument(
   ignoringParenImpCasts(declRefExpr(to(varDecl(hasName("y"));
   StatementMatcher CallArgumentY = callExpr(HasArgumentY);
+  StatementMatcher CtorArgumentY = cxxConstructExpr(HasArgumentY);
+  StatementMatcher UnresolvedCtorArgumentY =
+  cxxUnresolvedConstructExpr(HasArgumentY);
   StatementMatcher ObjCCallArgum

[PATCH] D50606: [ASTMatchers] Add matchers unresolvedMemberExpr, cxxDependentScopeMemberExpr

2018-08-12 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
Herald added a subscriber: cfe-commits.

Repository:
  rC Clang

https://reviews.llvm.org/D50606

Files:
  docs/LibASTMatchersReference.html
  include/clang/ASTMatchers/ASTMatchers.h
  lib/ASTMatchers/ASTMatchersInternal.cpp
  lib/ASTMatchers/Dynamic/Registry.cpp
  unittests/AST/ASTImporterTest.cpp
  unittests/ASTMatchers/ASTMatchersNodeTest.cpp

Index: unittests/ASTMatchers/ASTMatchersNodeTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -422,10 +422,17 @@
 
 TEST(MemberExpression, DoesNotMatchClasses) {
   EXPECT_TRUE(notMatches("class Y { void x() {} };", memberExpr()));
+  EXPECT_TRUE(notMatches("class Y { void x() {} };", unresolvedMemberExpr()));
+  EXPECT_TRUE(
+  notMatches("class Y { void x() {} };", cxxDependentScopeMemberExpr()));
 }
 
 TEST(MemberExpression, MatchesMemberFunctionCall) {
   EXPECT_TRUE(matches("class Y { void x() { x(); } };", memberExpr()));
+  EXPECT_TRUE(matches("class Y { template  void x() { x(); } };",
+  unresolvedMemberExpr()));
+  EXPECT_TRUE(matches("template  void x() { T t; t.f(); }",
+  cxxDependentScopeMemberExpr()));
 }
 
 TEST(MemberExpression, MatchesVariable) {
@@ -435,6 +442,13 @@
 matches("class Y { void x() { y; } int y; };", memberExpr()));
   EXPECT_TRUE(
 matches("class Y { void x() { Y y; y.y; } int y; };", memberExpr()));
+  EXPECT_TRUE(matches("template "
+  "class X : T { void f() { this->T::v; } };",
+  cxxDependentScopeMemberExpr()));
+  EXPECT_TRUE(matches("template  class X : T { void f() { T::v; } };",
+  cxxDependentScopeMemberExpr()));
+  EXPECT_TRUE(matches("template  void x() { T t; t.v; }",
+  cxxDependentScopeMemberExpr()));
 }
 
 TEST(MemberExpression, MatchesStaticVariable) {
Index: unittests/AST/ASTImporterTest.cpp
===
--- unittests/AST/ASTImporterTest.cpp
+++ unittests/AST/ASTImporterTest.cpp
@@ -814,9 +814,6 @@
   functionTemplateDecl());
 }
 
-const internal::VariadicDynCastAllOfMatcher
-cxxDependentScopeMemberExpr;
-
 TEST_P(ImportExpr, ImportCXXDependentScopeMemberExpr) {
   MatchVerifier Verifier;
   testImport(
@@ -2364,9 +2361,6 @@
  cxxRecordDecl(has(typedefDecl(has(dependentNameType(;
 }
 
-const internal::VariadicDynCastAllOfMatcher
-unresolvedMemberExpr;
-
 TEST_P(ImportExpr, UnresolvedMemberExpr) {
   MatchVerifier Verifier;
   testImport("struct S { template  void mem(); };"
Index: lib/ASTMatchers/Dynamic/Registry.cpp
===
--- lib/ASTMatchers/Dynamic/Registry.cpp
+++ lib/ASTMatchers/Dynamic/Registry.cpp
@@ -167,6 +167,7 @@
   REGISTER_MATCHER(cxxCtorInitializer);
   REGISTER_MATCHER(cxxDefaultArgExpr);
   REGISTER_MATCHER(cxxDeleteExpr);
+  REGISTER_MATCHER(cxxDependentScopeMemberExpr);
   REGISTER_MATCHER(cxxDestructorDecl);
   REGISTER_MATCHER(cxxDynamicCastExpr);
   REGISTER_MATCHER(cxxForRangeStmt);
@@ -475,6 +476,7 @@
   REGISTER_MATCHER(unaryTransformType);
   REGISTER_MATCHER(unless);
   REGISTER_MATCHER(unresolvedLookupExpr);
+  REGISTER_MATCHER(unresolvedMemberExpr);
   REGISTER_MATCHER(unresolvedUsingTypenameDecl);
   REGISTER_MATCHER(unresolvedUsingValueDecl);
   REGISTER_MATCHER(userDefinedLiteral);
Index: lib/ASTMatchers/ASTMatchersInternal.cpp
===
--- lib/ASTMatchers/ASTMatchersInternal.cpp
+++ lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -610,6 +610,10 @@
 const internal::VariadicAllOfMatcher stmt;
 const internal::VariadicDynCastAllOfMatcher declStmt;
 const internal::VariadicDynCastAllOfMatcher memberExpr;
+const internal::VariadicDynCastAllOfMatcher
+unresolvedMemberExpr;
+const internal::VariadicDynCastAllOfMatcher
+cxxDependentScopeMemberExpr;
 const internal::VariadicDynCastAllOfMatcher callExpr;
 const internal::VariadicDynCastAllOfMatcher lambdaExpr;
 const internal::VariadicDynCastAllOfMatcher
Index: include/clang/ASTMatchers/ASTMatchers.h
===
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -1141,6 +1141,34 @@
 ///   matches this->x, x, y.x, a, this->b
 extern const internal::VariadicDynCastAllOfMatcher memberExpr;
 
+/// Matches unresolved member expressions.
+///
+/// Given
+/// \code
+///   struct X {
+/// template  void f();
+/// void g();
+///   };
+///   template  void h() { X x; x.f(); x.g(); }
+/// \endcode
+/// unresolvedMemberExpr()
+///   matches x.f
+extern const internal::VariadicDynCastAllOfMatcher
+unresolvedMemberExpr;
+
+/// Matches member expressions where the actual member referenced could not be
+/// resolved because the base expression or the me

[PATCH] D50606: [ASTMatchers] Add matchers unresolvedMemberExpr, cxxDependentScopeMemberExpr

2018-08-12 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 160263.
shuaiwang marked 2 inline comments as done.
shuaiwang added a comment.

undo unrelated changes in doc


Repository:
  rC Clang

https://reviews.llvm.org/D50606

Files:
  docs/LibASTMatchersReference.html
  include/clang/ASTMatchers/ASTMatchers.h
  lib/ASTMatchers/ASTMatchersInternal.cpp
  lib/ASTMatchers/Dynamic/Registry.cpp
  unittests/AST/ASTImporterTest.cpp
  unittests/ASTMatchers/ASTMatchersNodeTest.cpp

Index: unittests/ASTMatchers/ASTMatchersNodeTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -422,10 +422,17 @@
 
 TEST(MemberExpression, DoesNotMatchClasses) {
   EXPECT_TRUE(notMatches("class Y { void x() {} };", memberExpr()));
+  EXPECT_TRUE(notMatches("class Y { void x() {} };", unresolvedMemberExpr()));
+  EXPECT_TRUE(
+  notMatches("class Y { void x() {} };", cxxDependentScopeMemberExpr()));
 }
 
 TEST(MemberExpression, MatchesMemberFunctionCall) {
   EXPECT_TRUE(matches("class Y { void x() { x(); } };", memberExpr()));
+  EXPECT_TRUE(matches("class Y { template  void x() { x(); } };",
+  unresolvedMemberExpr()));
+  EXPECT_TRUE(matches("template  void x() { T t; t.f(); }",
+  cxxDependentScopeMemberExpr()));
 }
 
 TEST(MemberExpression, MatchesVariable) {
@@ -435,6 +442,13 @@
 matches("class Y { void x() { y; } int y; };", memberExpr()));
   EXPECT_TRUE(
 matches("class Y { void x() { Y y; y.y; } int y; };", memberExpr()));
+  EXPECT_TRUE(matches("template "
+  "class X : T { void f() { this->T::v; } };",
+  cxxDependentScopeMemberExpr()));
+  EXPECT_TRUE(matches("template  class X : T { void f() { T::v; } };",
+  cxxDependentScopeMemberExpr()));
+  EXPECT_TRUE(matches("template  void x() { T t; t.v; }",
+  cxxDependentScopeMemberExpr()));
 }
 
 TEST(MemberExpression, MatchesStaticVariable) {
Index: unittests/AST/ASTImporterTest.cpp
===
--- unittests/AST/ASTImporterTest.cpp
+++ unittests/AST/ASTImporterTest.cpp
@@ -814,9 +814,6 @@
   functionTemplateDecl());
 }
 
-const internal::VariadicDynCastAllOfMatcher
-cxxDependentScopeMemberExpr;
-
 TEST_P(ImportExpr, ImportCXXDependentScopeMemberExpr) {
   MatchVerifier Verifier;
   testImport(
@@ -2364,9 +2361,6 @@
  cxxRecordDecl(has(typedefDecl(has(dependentNameType(;
 }
 
-const internal::VariadicDynCastAllOfMatcher
-unresolvedMemberExpr;
-
 TEST_P(ImportExpr, UnresolvedMemberExpr) {
   MatchVerifier Verifier;
   testImport("struct S { template  void mem(); };"
Index: lib/ASTMatchers/Dynamic/Registry.cpp
===
--- lib/ASTMatchers/Dynamic/Registry.cpp
+++ lib/ASTMatchers/Dynamic/Registry.cpp
@@ -167,6 +167,7 @@
   REGISTER_MATCHER(cxxCtorInitializer);
   REGISTER_MATCHER(cxxDefaultArgExpr);
   REGISTER_MATCHER(cxxDeleteExpr);
+  REGISTER_MATCHER(cxxDependentScopeMemberExpr);
   REGISTER_MATCHER(cxxDestructorDecl);
   REGISTER_MATCHER(cxxDynamicCastExpr);
   REGISTER_MATCHER(cxxForRangeStmt);
@@ -475,6 +476,7 @@
   REGISTER_MATCHER(unaryTransformType);
   REGISTER_MATCHER(unless);
   REGISTER_MATCHER(unresolvedLookupExpr);
+  REGISTER_MATCHER(unresolvedMemberExpr);
   REGISTER_MATCHER(unresolvedUsingTypenameDecl);
   REGISTER_MATCHER(unresolvedUsingValueDecl);
   REGISTER_MATCHER(userDefinedLiteral);
Index: lib/ASTMatchers/ASTMatchersInternal.cpp
===
--- lib/ASTMatchers/ASTMatchersInternal.cpp
+++ lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -610,6 +610,10 @@
 const internal::VariadicAllOfMatcher stmt;
 const internal::VariadicDynCastAllOfMatcher declStmt;
 const internal::VariadicDynCastAllOfMatcher memberExpr;
+const internal::VariadicDynCastAllOfMatcher
+unresolvedMemberExpr;
+const internal::VariadicDynCastAllOfMatcher
+cxxDependentScopeMemberExpr;
 const internal::VariadicDynCastAllOfMatcher callExpr;
 const internal::VariadicDynCastAllOfMatcher lambdaExpr;
 const internal::VariadicDynCastAllOfMatcher
Index: include/clang/ASTMatchers/ASTMatchers.h
===
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -1141,6 +1141,34 @@
 ///   matches this->x, x, y.x, a, this->b
 extern const internal::VariadicDynCastAllOfMatcher memberExpr;
 
+/// Matches unresolved member expressions.
+///
+/// Given
+/// \code
+///   struct X {
+/// template  void f();
+/// void g();
+///   };
+///   template  void h() { X x; x.f(); x.g(); }
+/// \endcode
+/// unresolvedMemberExpr()
+///   matches x.f
+extern const internal::VariadicDynCastAllOfMatcher
+unresolvedMemberExpr;
+
+/// Matches member expressions where the actual memb

[PATCH] D50606: [ASTMatchers] Add matchers unresolvedMemberExpr, cxxDependentScopeMemberExpr

2018-08-12 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL339522: [ASTMatchers] Add matchers unresolvedMemberExpr, 
cxxDependentScopeMemberExpr (authored by shuaiwang, committed by ).
Herald added a subscriber: llvm-commits.

Repository:
  rL LLVM

https://reviews.llvm.org/D50606

Files:
  cfe/trunk/docs/LibASTMatchersReference.html
  cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
  cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp
  cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp
  cfe/trunk/unittests/AST/ASTImporterTest.cpp
  cfe/trunk/unittests/ASTMatchers/ASTMatchersNodeTest.cpp

Index: cfe/trunk/docs/LibASTMatchersReference.html
===
--- cfe/trunk/docs/LibASTMatchersReference.html
+++ cfe/trunk/docs/LibASTMatchersReference.html
@@ -866,6 +866,17 @@
 
 
 
+MatcherStmt>cxxDependentScopeMemberExprMatcherCXXDependentScopeMemberExpr>...
+Matches member expressions where the actual member referenced could not be
+resolved because the base expression or the member name was dependent.
+
+Given
+  template  void f() { T t; t.g(); }
+cxxDependentScopeMemberExpr()
+  matches t.g
+
+
+
 MatcherStmt>cxxDynamicCastExprMatcherCXXDynamicCastExpr>...
 Matches a dynamic_cast expression.
 
@@ -1455,6 +1466,20 @@
   matches foo() 
 
 
+MatcherStmt>unresolvedMemberExprMatcherUnresolvedMemberExpr>...
+Matches unresolved member expressions.
+
+Given
+  struct X {
+template  void f();
+void g();
+  };
+  template  void h() { X x; x.f(); x.g(); }
+unresolvedMemberExpr()
+  matches x.f
+
+
+
 MatcherStmt>userDefinedLiteralMatcherUserDefinedLiteral>...
 Matches user defined literal operator call.
 
@@ -1598,7 +1623,7 @@
   short i = 1;
   int j = 42;
   decltype(i + j) result = i + j;
-decltypeType() 
+decltypeType()
   matches "decltype(i + j)"
 
 
Index: cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
===
--- cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
+++ cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
@@ -1141,6 +1141,34 @@
 ///   matches this->x, x, y.x, a, this->b
 extern const internal::VariadicDynCastAllOfMatcher memberExpr;
 
+/// Matches unresolved member expressions.
+///
+/// Given
+/// \code
+///   struct X {
+/// template  void f();
+/// void g();
+///   };
+///   template  void h() { X x; x.f(); x.g(); }
+/// \endcode
+/// unresolvedMemberExpr()
+///   matches x.f
+extern const internal::VariadicDynCastAllOfMatcher
+unresolvedMemberExpr;
+
+/// Matches member expressions where the actual member referenced could not be
+/// resolved because the base expression or the member name was dependent.
+///
+/// Given
+/// \code
+///   template  void f() { T t; t.g(); }
+/// \endcode
+/// cxxDependentScopeMemberExpr()
+///   matches t.g
+extern const internal::VariadicDynCastAllOfMatcher
+cxxDependentScopeMemberExpr;
+
 /// Matches call expressions.
 ///
 /// Example matches x.y() and y()
Index: cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp
===
--- cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp
+++ cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -167,6 +167,7 @@
   REGISTER_MATCHER(cxxCtorInitializer);
   REGISTER_MATCHER(cxxDefaultArgExpr);
   REGISTER_MATCHER(cxxDeleteExpr);
+  REGISTER_MATCHER(cxxDependentScopeMemberExpr);
   REGISTER_MATCHER(cxxDestructorDecl);
   REGISTER_MATCHER(cxxDynamicCastExpr);
   REGISTER_MATCHER(cxxForRangeStmt);
@@ -475,6 +476,7 @@
   REGISTER_MATCHER(unaryTransformType);
   REGISTER_MATCHER(unless);
   REGISTER_MATCHER(unresolvedLookupExpr);
+  REGISTER_MATCHER(unresolvedMemberExpr);
   REGISTER_MATCHER(unresolvedUsingTypenameDecl);
   REGISTER_MATCHER(unresolvedUsingValueDecl);
   REGISTER_MATCHER(userDefinedLiteral);
Index: cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp
===
--- cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ cfe/trunk/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -610,6 +610,10 @@
 const internal::VariadicAllOfMatcher stmt;
 const internal::VariadicDynCastAllOfMatcher declStmt;
 const internal::VariadicDynCastAllOfMatcher memberExpr;
+const internal::VariadicDynCastAllOfMatcher
+unresolvedMemberExpr;
+const internal::VariadicDynCastAllOfMatcher
+cxxDependentScopeMemberExpr;
 const interna

[PATCH] D50605: [ASTMatchers] Let hasAnyArgument also support CXXUnresolvedConstructExpr

2018-08-12 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 160265.
shuaiwang added a comment.

update ast matchers doc


Repository:
  rC Clang

https://reviews.llvm.org/D50605

Files:
  docs/LibASTMatchersReference.html
  include/clang/AST/ExprCXX.h
  include/clang/ASTMatchers/ASTMatchers.h
  unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Index: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -406,16 +406,35 @@
   auto HasArgumentY = hasAnyArgument(
   ignoringParenImpCasts(declRefExpr(to(varDecl(hasName("y"));
   StatementMatcher CallArgumentY = callExpr(HasArgumentY);
+  StatementMatcher CtorArgumentY = cxxConstructExpr(HasArgumentY);
+  StatementMatcher UnresolvedCtorArgumentY =
+  cxxUnresolvedConstructExpr(HasArgumentY);
   StatementMatcher ObjCCallArgumentY = objcMessageExpr(HasArgumentY);
   EXPECT_TRUE(matches("void x(int, int) { int y; x(1, y); }", CallArgumentY));
   EXPECT_TRUE(matches("void x(int, int) { int y; x(y, 42); }", CallArgumentY));
+  EXPECT_TRUE(matches("struct Y { Y(int, int); };"
+  "void x() { int y; (void)Y(1, y); }",
+  CtorArgumentY));
+  EXPECT_TRUE(matches("struct Y { Y(int, int); };"
+  "void x() { int y; (void)Y(y, 42); }",
+  CtorArgumentY));
+  EXPECT_TRUE(matches("template  void x() { int y; (void)Y(1, y); }",
+  UnresolvedCtorArgumentY));
+  EXPECT_TRUE(matches("template  void x() { int y; (void)Y(y, 42); }",
+  UnresolvedCtorArgumentY));
   EXPECT_TRUE(matchesObjC("@interface I -(void)f:(int) y; @end "
   "void x(I* i) { int y; [i f:y]; }",
   ObjCCallArgumentY));
   EXPECT_FALSE(matchesObjC("@interface I -(void)f:(int) z; @end "
"void x(I* i) { int z; [i f:z]; }",
ObjCCallArgumentY));
   EXPECT_TRUE(notMatches("void x(int, int) { x(1, 2); }", CallArgumentY));
+  EXPECT_TRUE(notMatches("struct Y { Y(int, int); };"
+ "void x() { int y; (void)Y(1, 2); }",
+ CtorArgumentY));
+  EXPECT_TRUE(notMatches("template "
+ "void x() { int y; (void)Y(1, 2); }",
+ UnresolvedCtorArgumentY));
 
   StatementMatcher ImplicitCastedArgument = callExpr(
 hasAnyArgument(implicitCastExpr()));
Index: include/clang/ASTMatchers/ASTMatchers.h
===
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -3565,9 +3565,9 @@
 /// objcMessageExpr(hasAnyArgument(integerLiteral(equals(12
 ///   matches [i f:12]
 AST_POLYMORPHIC_MATCHER_P(hasAnyArgument,
-  AST_POLYMORPHIC_SUPPORTED_TYPES(CallExpr,
-  CXXConstructExpr,
-  ObjCMessageExpr),
+  AST_POLYMORPHIC_SUPPORTED_TYPES(
+  CallExpr, CXXConstructExpr,
+  CXXUnresolvedConstructExpr, ObjCMessageExpr),
   internal::Matcher, InnerMatcher) {
   for (const Expr *Arg : Node.arguments()) {
 BoundNodesTreeBuilder Result(*Builder);
Index: include/clang/AST/ExprCXX.h
===
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -3426,16 +3426,22 @@
   unsigned arg_size() const { return NumArgs; }
 
   using arg_iterator = Expr **;
+  using arg_range = llvm::iterator_range;
 
   arg_iterator arg_begin() { return getTrailingObjects(); }
   arg_iterator arg_end() { return arg_begin() + NumArgs; }
+  arg_range arguments() { return arg_range(arg_begin(), arg_end()); }
 
   using const_arg_iterator = const Expr* const *;
+  using arg_const_range = llvm::iterator_range;
 
   const_arg_iterator arg_begin() const { return getTrailingObjects(); }
   const_arg_iterator arg_end() const {
 return arg_begin() + NumArgs;
   }
+  arg_const_range arguments() const {
+return arg_const_range(arg_begin(), arg_end());
+  }
 
   Expr *getArg(unsigned I) {
 assert(I < NumArgs && "Argument index out-of-range");
Index: docs/LibASTMatchersReference.html
===
--- docs/LibASTMatchersReference.html
+++ docs/LibASTMatchersReference.html
@@ -4854,6 +4854,25 @@
 
 
 
+MatcherCXXUnresolvedConstructExpr>hasAnyArgumentMatcherExpr> InnerMatcher
+Matches any argument of a call expression or a constructor call
+expression, or an ObjC-message-send expression.
+
+Given
+  void x(int, 

[PATCH] D50605: [ASTMatchers] Let hasAnyArgument also support CXXUnresolvedConstructExpr

2018-08-12 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang marked an inline comment as done.
shuaiwang added inline comments.



Comment at: include/clang/AST/ExprCXX.h:3436
   using const_arg_iterator = const Expr* const *;
+  using arg_const_range = llvm::iterator_range;
 

aaron.ballman wrote:
> Please name this `const_arg_range` for consistency.
`arg_const_range` is more widely in clang


Repository:
  rC Clang

https://reviews.llvm.org/D50605



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50605: [ASTMatchers] Let hasAnyArgument also support CXXUnresolvedConstructExpr

2018-08-12 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 160273.
shuaiwang marked 3 inline comments as done.
shuaiwang added a comment.

arg_const_range -> const_arg_range


Repository:
  rC Clang

https://reviews.llvm.org/D50605

Files:
  docs/LibASTMatchersReference.html
  include/clang/AST/ExprCXX.h
  include/clang/ASTMatchers/ASTMatchers.h
  unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Index: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -406,16 +406,35 @@
   auto HasArgumentY = hasAnyArgument(
   ignoringParenImpCasts(declRefExpr(to(varDecl(hasName("y"));
   StatementMatcher CallArgumentY = callExpr(HasArgumentY);
+  StatementMatcher CtorArgumentY = cxxConstructExpr(HasArgumentY);
+  StatementMatcher UnresolvedCtorArgumentY =
+  cxxUnresolvedConstructExpr(HasArgumentY);
   StatementMatcher ObjCCallArgumentY = objcMessageExpr(HasArgumentY);
   EXPECT_TRUE(matches("void x(int, int) { int y; x(1, y); }", CallArgumentY));
   EXPECT_TRUE(matches("void x(int, int) { int y; x(y, 42); }", CallArgumentY));
+  EXPECT_TRUE(matches("struct Y { Y(int, int); };"
+  "void x() { int y; (void)Y(1, y); }",
+  CtorArgumentY));
+  EXPECT_TRUE(matches("struct Y { Y(int, int); };"
+  "void x() { int y; (void)Y(y, 42); }",
+  CtorArgumentY));
+  EXPECT_TRUE(matches("template  void x() { int y; (void)Y(1, y); }",
+  UnresolvedCtorArgumentY));
+  EXPECT_TRUE(matches("template  void x() { int y; (void)Y(y, 42); }",
+  UnresolvedCtorArgumentY));
   EXPECT_TRUE(matchesObjC("@interface I -(void)f:(int) y; @end "
   "void x(I* i) { int y; [i f:y]; }",
   ObjCCallArgumentY));
   EXPECT_FALSE(matchesObjC("@interface I -(void)f:(int) z; @end "
"void x(I* i) { int z; [i f:z]; }",
ObjCCallArgumentY));
   EXPECT_TRUE(notMatches("void x(int, int) { x(1, 2); }", CallArgumentY));
+  EXPECT_TRUE(notMatches("struct Y { Y(int, int); };"
+ "void x() { int y; (void)Y(1, 2); }",
+ CtorArgumentY));
+  EXPECT_TRUE(notMatches("template "
+ "void x() { int y; (void)Y(1, 2); }",
+ UnresolvedCtorArgumentY));
 
   StatementMatcher ImplicitCastedArgument = callExpr(
 hasAnyArgument(implicitCastExpr()));
Index: include/clang/ASTMatchers/ASTMatchers.h
===
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -3565,9 +3565,9 @@
 /// objcMessageExpr(hasAnyArgument(integerLiteral(equals(12
 ///   matches [i f:12]
 AST_POLYMORPHIC_MATCHER_P(hasAnyArgument,
-  AST_POLYMORPHIC_SUPPORTED_TYPES(CallExpr,
-  CXXConstructExpr,
-  ObjCMessageExpr),
+  AST_POLYMORPHIC_SUPPORTED_TYPES(
+  CallExpr, CXXConstructExpr,
+  CXXUnresolvedConstructExpr, ObjCMessageExpr),
   internal::Matcher, InnerMatcher) {
   for (const Expr *Arg : Node.arguments()) {
 BoundNodesTreeBuilder Result(*Builder);
Index: include/clang/AST/ExprCXX.h
===
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -3426,16 +3426,22 @@
   unsigned arg_size() const { return NumArgs; }
 
   using arg_iterator = Expr **;
+  using arg_range = llvm::iterator_range;
 
   arg_iterator arg_begin() { return getTrailingObjects(); }
   arg_iterator arg_end() { return arg_begin() + NumArgs; }
+  arg_range arguments() { return arg_range(arg_begin(), arg_end()); }
 
   using const_arg_iterator = const Expr* const *;
+  using const_arg_range = llvm::iterator_range;
 
   const_arg_iterator arg_begin() const { return getTrailingObjects(); }
   const_arg_iterator arg_end() const {
 return arg_begin() + NumArgs;
   }
+  const_arg_range arguments() const {
+return const_arg_range(arg_begin(), arg_end());
+  }
 
   Expr *getArg(unsigned I) {
 assert(I < NumArgs && "Argument index out-of-range");
Index: docs/LibASTMatchersReference.html
===
--- docs/LibASTMatchersReference.html
+++ docs/LibASTMatchersReference.html
@@ -4854,6 +4854,25 @@
 
 
 
+MatcherCXXUnresolvedConstructExpr>hasAnyArgumentMatcherExpr> InnerMatcher
+Matches any argument of a call expression or a constructor call
+expression, or an

[PATCH] D50617: [ASTMatchers] Let hasObjectExpression also support UnresolvedMemberExpr, CXXDependentScopeMemberExpr

2018-08-12 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
shuaiwang added a reviewer: aaron.ballman.
Herald added a subscriber: cfe-commits.

Repository:
  rC Clang

https://reviews.llvm.org/D50617

Files:
  docs/LibASTMatchersReference.html
  include/clang/ASTMatchers/ASTMatchers.h
  unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp


Index: unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1517,6 +1517,26 @@
 "struct X { int m; }; void f(X* x) { x->m; }",
 memberExpr(hasObjectExpression(
   hasType(pointsTo(recordDecl(hasName("X";
+  EXPECT_TRUE(matches("template  struct X { void f() { T t; t.m; } 
};",
+  cxxDependentScopeMemberExpr(hasObjectExpression(
+  declRefExpr(to(namedDecl(hasName("t";
+  EXPECT_TRUE(
+  matches("template  struct X { void f() { T t; t->m; } };",
+  cxxDependentScopeMemberExpr(hasObjectExpression(
+  declRefExpr(to(namedDecl(hasName("t";
+}
+
+TEST(HasObjectExpression, MatchesBaseOfMemberFunc) {
+  EXPECT_TRUE(matches(
+  "struct X { void f(); }; void g(X x) { x.f(); }",
+  memberExpr(hasObjectExpression(hasType(recordDecl(hasName("X")));
+  EXPECT_TRUE(matches("struct X { template  void f(); };"
+  "template  void g(X x) { x.f(); }",
+  unresolvedMemberExpr(hasObjectExpression(
+  hasType(recordDecl(hasName("X")));
+  EXPECT_TRUE(matches("template  void f(T t) { t.g(); }",
+  cxxDependentScopeMemberExpr(hasObjectExpression(
+  declRefExpr(to(namedDecl(hasName("t";
 }
 
 TEST(HasObjectExpression,
Index: include/clang/ASTMatchers/ASTMatchers.h
===
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -4806,8 +4806,11 @@
 ///   matches "x.m" and "m"
 /// with hasObjectExpression(...)
 ///   matching "x" and the implicit object expression of "m" which has type X*.
-AST_MATCHER_P(MemberExpr, hasObjectExpression,
-  internal::Matcher, InnerMatcher) {
+AST_POLYMORPHIC_MATCHER_P(
+hasObjectExpression,
+AST_POLYMORPHIC_SUPPORTED_TYPES(MemberExpr, UnresolvedMemberExpr,
+CXXDependentScopeMemberExpr),
+internal::Matcher, InnerMatcher) {
   return InnerMatcher.matches(*Node.getBase(), Finder, Builder);
 }
 
Index: docs/LibASTMatchersReference.html
===
--- docs/LibASTMatchersReference.html
+++ docs/LibASTMatchersReference.html
@@ -4668,6 +4668,20 @@
 
 
 
+MatcherCXXDependentScopeMemberExpr>hasObjectExpressionMatcherExpr> 
InnerMatcher
+Matches a 
member expression where the object expression is
+matched by a given matcher.
+
+Given
+  struct X { int m; };
+  void f(X x) { x.m; m; }
+memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))
+  matches "x.m" and "m"
+with hasObjectExpression(...)
+  matching "x" and the implicit object expression of "m" which has type X*.
+
+
+
 MatcherCXXForRangeStmt>hasBodyMatcherStmt> 
InnerMatcher
 Matches a 'for', 'while', 
'do while' statement or a function
 definition that has a given body.
@@ -6673,6 +6687,20 @@
 
 
 
+MatcherUnresolvedMemberExpr>hasObjectExpressionMatcherExpr> 
InnerMatcher
+Matches a 
member expression where the object expression is
+matched by a given matcher.
+
+Given
+  struct X { int m; };
+  void f(X x) { x.m; m; }
+memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))
+  matches "x.m" and "m"
+with hasObjectExpression(...)
+  matching "x" and the implicit object expression of "m" which has type X*.
+
+
+
 MatcherUnresolvedUsingType>hasDeclarationconst MatcherDecl>  
InnerMatcher
 Matches a node if 
the declaration associated with that node
 matches the given matcher.


Index: unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1517,6 +1517,26 @@
 "struct X { int m; }; void f(X* x) { x->m; }",
 memberExpr(hasObjectExpression(
   hasType(pointsTo(recordDecl(hasName("X";
+  E

[PATCH] D50605: [ASTMatchers] Let hasAnyArgument also support CXXUnresolvedConstructExpr

2018-08-12 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL339530: [ASTMatchers] Let hasAnyArgument also support 
CXXUnresolvedConstructExpr (authored by shuaiwang, committed by ).
Herald added a subscriber: llvm-commits.

Repository:
  rL LLVM

https://reviews.llvm.org/D50605

Files:
  cfe/trunk/docs/LibASTMatchersReference.html
  cfe/trunk/include/clang/AST/ExprCXX.h
  cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
  cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp

Index: cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===
--- cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ cfe/trunk/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -406,16 +406,35 @@
   auto HasArgumentY = hasAnyArgument(
   ignoringParenImpCasts(declRefExpr(to(varDecl(hasName("y"));
   StatementMatcher CallArgumentY = callExpr(HasArgumentY);
+  StatementMatcher CtorArgumentY = cxxConstructExpr(HasArgumentY);
+  StatementMatcher UnresolvedCtorArgumentY =
+  cxxUnresolvedConstructExpr(HasArgumentY);
   StatementMatcher ObjCCallArgumentY = objcMessageExpr(HasArgumentY);
   EXPECT_TRUE(matches("void x(int, int) { int y; x(1, y); }", CallArgumentY));
   EXPECT_TRUE(matches("void x(int, int) { int y; x(y, 42); }", CallArgumentY));
+  EXPECT_TRUE(matches("struct Y { Y(int, int); };"
+  "void x() { int y; (void)Y(1, y); }",
+  CtorArgumentY));
+  EXPECT_TRUE(matches("struct Y { Y(int, int); };"
+  "void x() { int y; (void)Y(y, 42); }",
+  CtorArgumentY));
+  EXPECT_TRUE(matches("template  void x() { int y; (void)Y(1, y); }",
+  UnresolvedCtorArgumentY));
+  EXPECT_TRUE(matches("template  void x() { int y; (void)Y(y, 42); }",
+  UnresolvedCtorArgumentY));
   EXPECT_TRUE(matchesObjC("@interface I -(void)f:(int) y; @end "
   "void x(I* i) { int y; [i f:y]; }",
   ObjCCallArgumentY));
   EXPECT_FALSE(matchesObjC("@interface I -(void)f:(int) z; @end "
"void x(I* i) { int z; [i f:z]; }",
ObjCCallArgumentY));
   EXPECT_TRUE(notMatches("void x(int, int) { x(1, 2); }", CallArgumentY));
+  EXPECT_TRUE(notMatches("struct Y { Y(int, int); };"
+ "void x() { int y; (void)Y(1, 2); }",
+ CtorArgumentY));
+  EXPECT_TRUE(notMatches("template "
+ "void x() { int y; (void)Y(1, 2); }",
+ UnresolvedCtorArgumentY));
 
   StatementMatcher ImplicitCastedArgument = callExpr(
 hasAnyArgument(implicitCastExpr()));
Index: cfe/trunk/docs/LibASTMatchersReference.html
===
--- cfe/trunk/docs/LibASTMatchersReference.html
+++ cfe/trunk/docs/LibASTMatchersReference.html
@@ -4854,6 +4854,25 @@
 
 
 
+MatcherCXXUnresolvedConstructExpr>hasAnyArgumentMatcherExpr> InnerMatcher
+Matches any argument of a call expression or a constructor call
+expression, or an ObjC-message-send expression.
+
+Given
+  void x(int, int, int) { int y; x(1, y, 42); }
+callExpr(hasAnyArgument(declRefExpr()))
+  matches x(1, y, 42)
+with hasAnyArgument(...)
+  matching y
+
+For ObjectiveC, given
+  @interface I - (void) f:(int) y; @end
+  void foo(I *i) { [i f:12]; }
+objcMessageExpr(hasAnyArgument(integerLiteral(equals(12
+  matches [i f:12]
+
+
+
 MatcherCallExpr>calleeMatcherDecl> InnerMatcher
 Matches if the call expression's callee's declaration matches the
 given matcher.
@@ -5938,8 +5957,8 @@
 
 
 
-MatcherObjCMessageExpr>hasAnyArgumentMatcherExpr> InnerMatcher
-Matches any argument of a call expression or a constructor call
+MatcherObjCMessageExpr>hasAnyArgumentMatcherExpr> InnerMatcher
+Matches any argument of a call expression or a constructor call
 expression, or an ObjC-message-send expression.
 
 Given
Index: cfe/trunk/include/clang/AST/ExprCXX.h
===
--- cfe/trunk/include/clang/AST/ExprCXX.h
+++ cfe/trunk/include/clang/AST/ExprCXX.h
@@ -3426,16 +3426,22 @@
   unsigned arg_size() const { return NumArgs; }
 
   using arg_iterator = Expr **;
+  using arg_range = llvm::iterator_range;
 
   arg_iterator arg_begin() { return getTrailingObjects(); }
   arg_iterator arg_end() { return arg_begin() + NumAr

[PATCH] D50619: [clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer

2018-08-12 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
shuaiwang added reviewers: aaron.ballman, JonasToth.
Herald added subscribers: cfe-commits, a.sidorin, xazax.hun.
Herald added a reviewer: george.karpenkov.

- If a function is unresolved, assume it mutates its arguments
- Follow unresolved member expressions for nested mutations


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50619

Files:
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -114,6 +114,20 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
 }
 
+TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) {
+  auto AST = tooling::buildASTFromCode(
+  "struct X { template  void mf(); };"
+  "template  void f() { X x; x.mf(); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+
+  AST =
+  tooling::buildASTFromCode("template  void f() { T x; x.mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+}
+
 TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
   const auto AST = tooling::buildASTFromCode(
   "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
@@ -292,6 +306,24 @@
   ElementsAre("std::forward(x)"));
 }
 
+TEST(ExprMutationAnalyzerTest, CallUnresolved) {
+  auto AST =
+  tooling::buildASTFromCode("template  void f() { T x; g(x); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f() { int x; g(T(), x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(T(), x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f() { int x; (void)T(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)"));
+}
+
 TEST(ExprMutationAnalyzerTest, ReturnAsValue) {
   const auto AST = tooling::buildASTFromCode("int f() { int x; return x; }");
   const auto Results =
@@ -398,21 +430,31 @@
 }
 
 TEST(ExprMutationAnalyzerTest, NestedMemberModified) {
-  const auto AST = tooling::buildASTFromCode(
+  auto AST = tooling::buildASTFromCode(
   "void f() { struct A { int vi; }; struct B { A va; }; "
   "struct C { B vb; }; C x; x.vb.va.vi = 10; }");
-  const auto Results =
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.vb.va.vi = 10"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f() { T x; x.y.z = 10; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10"));
 }
 
 TEST(ExprMutationAnalyzerTest, NestedMemberNotModified) {
-  const auto AST = tooling::buildASTFromCode(
+  auto AST = tooling::buildASTFromCode(
   "void f() { struct A { int vi; }; struct B { A va; }; "
   "struct C { B vb; }; C x; x.vb.va.vi; }");
-  const auto Results =
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST =
+  tooling::buildASTFromCode("template  void f() { T x; x.y.z; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
 TEST(ExprMutationAnalyzerTest, CastToValue) {
Index: clang-tidy/utils/ExprMutationAnalyzer.cpp
===
--- clang-tidy/utils/ExprMutationAnalyzer.cpp
+++ clang-tidy/utils/ExprMutationAnalyzer.cpp
@@ -145,11 +145,16 @@
 hasUnaryOperand(equalsNode(Exp)));
 
   // Invoking non-const member function.
+  // A member function is assumed to be non-const when it is unresolved.
   const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
   const auto AsNonConstThis =
   expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))),
  cxxOperatorCallExpr(callee(NonConstMethod),
- hasArgument(0, equalsNode(Exp);
+ hasArgument(0, equalsNode(Exp))),
+ callExpr(callee(expr(anyOf(
+ unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))),
+ cxxDependentScopeMemberExpr(
+

[PATCH] D50619: [clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer

2018-08-14 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 160694.
shuaiwang marked 2 inline comments as done.
shuaiwang added a comment.
Herald added a subscriber: Szelethus.

- Fix a few cases overlooked previously
- More test cases


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50619

Files:
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -114,6 +114,20 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
 }
 
+TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) {
+  auto AST = tooling::buildASTFromCode(
+  "struct X { template  void mf(); };"
+  "template  void f() { X x; x.mf(); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+
+  AST =
+  tooling::buildASTFromCode("template  void f() { T x; x.mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+}
+
 TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
   const auto AST = tooling::buildASTFromCode(
   "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
@@ -292,6 +306,40 @@
   ElementsAre("std::forward(x)"));
 }
 
+TEST(ExprMutationAnalyzerTest, CallUnresolved) {
+  auto AST =
+  tooling::buildASTFromCode("template  void f() { T x; g(x); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f(T t) { int x; g(t, x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(t, x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void g(T t) { int x; t.mf(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("t.mf(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "struct S { template  void mf(); };"
+  "template  void f(S s) { int x; s.mf(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)"));
+
+  AST = tooling::buildASTFromCode("template "
+  "void g(F f) { int x; f(x); } ");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f() { int x; (void)T(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)"));
+}
+
 TEST(ExprMutationAnalyzerTest, ReturnAsValue) {
   const auto AST = tooling::buildASTFromCode("int f() { int x; return x; }");
   const auto Results =
@@ -347,6 +395,21 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
 }
 
+TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) {
+  const auto AST = tooling::buildASTFromCode(
+  "template  struct S { static constexpr int v = 8; };"
+  "template <> struct S { static constexpr int v = 4; };"
+  "void g(char*);"
+  "template  void f() { char x[S::v]; g(x); }"
+  "template <> void f() { char y[S::v]; g(y); }");
+  const auto ResultsX =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(ResultsX, AST.get()), ElementsAre("g(x)"));
+  const auto ResultsY =
+  match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(ResultsY, AST.get()), ElementsAre("y"));
+}
+
 TEST(ExprMutationAnalyzerTest, FollowRefModified) {
   const auto AST = tooling::buildASTFromCode(
   "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; "
@@ -398,21 +461,31 @@
 }
 
 TEST(ExprMutationAnalyzerTest, NestedMemberModified) {
-  const auto AST = tooling::buildASTFromCode(
+  auto AST = tooling::buildASTFromCode(
   "void f() { struct A { int vi; }; struct B { A va; }; "
   "struct C { B vb; }; C x; x.vb.va.vi = 10; }");
-  const auto Results =
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.vb.va.vi = 10"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f() { T x; x.y.z = 10; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10"));
 }
 

[PATCH] D50619: [clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer

2018-08-15 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added inline comments.



Comment at: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp:410
+  match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(ResultsY, AST.get()), ElementsAre("y"));
+}

JonasToth wrote:
> Out of curiosity: Why is the result with `y` different from the result for 
> `x`? Both time `x` is mutated and `g()` mutates them.
This is ultimately caused by not handling pointers yet.
As soon as the address of an object is taken we assume the object is mutated.
e.g.
```
void f(const int*);
void g() {
  int x;
  f(&x); // <-- address of x taken, assume mutation
  int y[10];
  f(y); // <-- address of y taken, assume mutation
}
```
And in all such cases the "mutated by" expression is the expression that takes 
the address.

So back in this case, `g(x)` mutates `x` because we're assuming `g` mutates its 
argument through non-const reference. Note that the declared `g` might not be 
the one actually being called because of overload resolution, there could be 
another `void g(char(&)[8])`
While for `g(y)` we know it's calling the `void g(char*)` so there's an array 
to pointer decay, and the decay is the point we assumed mutation not the 
function call.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50619



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50619: [clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer

2018-08-15 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 160952.
shuaiwang marked 3 inline comments as done.
shuaiwang added a comment.

More test cases


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50619

Files:
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -114,6 +114,26 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
 }
 
+TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) {
+  auto AST = tooling::buildASTFromCode(
+  "struct X { template  void mf(); };"
+  "template  void f() { X x; x.mf(); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+
+  AST =
+  tooling::buildASTFromCode("template  void f() { T x; x.mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+
+  AST = tooling::buildASTFromCode(
+  "template  struct X;"
+  "template  void f() { X x; x.mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+}
+
 TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
   const auto AST = tooling::buildASTFromCode(
   "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
@@ -292,6 +312,46 @@
   ElementsAre("std::forward(x)"));
 }
 
+TEST(ExprMutationAnalyzerTest, CallUnresolved) {
+  auto AST =
+  tooling::buildASTFromCode("template  void f() { T x; g(x); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f(T t) { int x; g(t, x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(t, x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f(T t) { int x; t.mf(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("t.mf(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  struct S;"
+  "template  void f() { S s; int x; s.mf(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "struct S { template  void mf(); };"
+  "template  void f(S s) { int x; s.mf(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)"));
+
+  AST = tooling::buildASTFromCode("template "
+  "void g(F f) { int x; f(x); } ");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f() { int x; (void)T(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)"));
+}
+
 TEST(ExprMutationAnalyzerTest, ReturnAsValue) {
   const auto AST = tooling::buildASTFromCode("int f() { int x; return x; }");
   const auto Results =
@@ -347,6 +407,21 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
 }
 
+TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) {
+  const auto AST = tooling::buildASTFromCode(
+  "template  struct S { static constexpr int v = 8; };"
+  "template <> struct S { static constexpr int v = 4; };"
+  "void g(char*);"
+  "template  void f() { char x[S::v]; g(x); }"
+  "template <> void f() { char y[S::v]; g(y); }");
+  const auto ResultsX =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(ResultsX, AST.get()), ElementsAre("g(x)"));
+  const auto ResultsY =
+  match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(ResultsY, AST.get()), ElementsAre("y"));
+}
+
 TEST(ExprMutationAnalyzerTest, FollowRefModified) {
   const auto AST = tooling::buildASTFromCode(
   "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; "
@@ -398,21 +473,43 @@
 }
 
 TEST(ExprMutationAnalyzerTest, NestedMemberModified) {
-  const auto AST = tooling::buildASTFromCode(
+  auto AST = tooling::buildASTFromCode(
   "void f() { struct A { int vi; }; struct B { A va; }; "
   "struct C { B vb; }; C x; x.vb.va.

[PATCH] D50619: [clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer

2018-08-15 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added inline comments.



Comment at: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp:309
 
+TEST(ExprMutationAnalyzerTest, CallUnresolved) {
+  auto AST =

JonasToth wrote:
> I think we are missing tests for non-type template paramters (`template 
> `). They should behave the same. But the following case would not 
> be a mutation:
> 
> ```
> void non_mutating(const char* Array, int size) { /* Foo */ }
> template 
> struct ArrayLike {
>   char* data[N]; // Initialized to something
>   void SomeFunction() {
> non_mutating(data, N);
>   }
> };
> ```
> 
> The difference between the 'normal' and non-type templates would be, that `N` 
> is not mutatable at all and the semantics is clear (builtin integer-like 
> type).
> 
> If the current implementation would not figure that out, you can just add a 
> test for it and assume a mutation. Handling non-type templates later is 
> absolutly ok.
We have to assume `data` is mutated here as well. I'll add a test case for this.

```
void g(const char*, int); // <-- doesn't mutate
void g(char(&)[8], int); // <-- do mutate

template 
void f() {
char data[N];
g(data, N); // <-- we don't know which `g` will be called yet
}

void h() {
f<8>(); // <-- f calls g(char(&)[8], int) internally
f<9>(); // <-- f calls g(const char*, int) internally
}
```




Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50619



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50619: [clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer

2018-08-15 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 160960.
shuaiwang added a comment.

Test case with non-type template


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50619

Files:
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -114,6 +114,26 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
 }
 
+TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) {
+  auto AST = tooling::buildASTFromCode(
+  "struct X { template  void mf(); };"
+  "template  void f() { X x; x.mf(); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+
+  AST =
+  tooling::buildASTFromCode("template  void f() { T x; x.mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+
+  AST = tooling::buildASTFromCode(
+  "template  struct X;"
+  "template  void f() { X x; x.mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+}
+
 TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
   const auto AST = tooling::buildASTFromCode(
   "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
@@ -292,6 +312,51 @@
   ElementsAre("std::forward(x)"));
 }
 
+TEST(ExprMutationAnalyzerTest, CallUnresolved) {
+  auto AST =
+  tooling::buildASTFromCode("template  void f() { T x; g(x); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f() { char x[N]; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f(T t) { int x; g(t, x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(t, x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f(T t) { int x; t.mf(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("t.mf(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  struct S;"
+  "template  void f() { S s; int x; s.mf(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "struct S { template  void mf(); };"
+  "template  void f(S s) { int x; s.mf(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)"));
+
+  AST = tooling::buildASTFromCode("template "
+  "void g(F f) { int x; f(x); } ");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f() { int x; (void)T(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)"));
+}
+
 TEST(ExprMutationAnalyzerTest, ReturnAsValue) {
   const auto AST = tooling::buildASTFromCode("int f() { int x; return x; }");
   const auto Results =
@@ -347,6 +412,21 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
 }
 
+TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) {
+  const auto AST = tooling::buildASTFromCode(
+  "template  struct S { static constexpr int v = 8; };"
+  "template <> struct S { static constexpr int v = 4; };"
+  "void g(char*);"
+  "template  void f() { char x[S::v]; g(x); }"
+  "template <> void f() { char y[S::v]; g(y); }");
+  const auto ResultsX =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(ResultsX, AST.get()), ElementsAre("g(x)"));
+  const auto ResultsY =
+  match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(ResultsY, AST.get()), ElementsAre("y"));
+}
+
 TEST(ExprMutationAnalyzerTest, FollowRefModified) {
   const auto AST = tooling::buildASTFromCode(
   "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; "
@@ -398,21 +478,43 @@
 }
 
 TEST(ExprMutationAnalyzerTest, Neste

[PATCH] D50619: [clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer

2018-08-16 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D50619#1202135, @JonasToth wrote:

> @shuaiwang i tried to apply this and check the clang-tidy part again, but it 
> does not compile (log attached).
>  I update clang to master, did you add a matcher or something like this?
>
> F6950472: error.log 


Oops, my bad, this depends on https://reviews.llvm.org/D50617


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50619



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50883: [clang-tidy] Handle unique owning smart pointers in ExprMutationAnalyzer

2018-08-16 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
shuaiwang added a reviewer: hokein.
Herald added subscribers: cfe-commits, Szelethus, a.sidorin, xazax.hun.
Herald added a reviewer: george.karpenkov.

For smart pointers like std::unique_ptr which uniquely owns the
underlying object, treat the mutation of the pointee as mutation of the
smart pointer itself.

This gives better behavior for cases like this:

  void f(std::vector> v) { // undesirable analyze result 
of `v` as not mutated.
for (auto& p : v) {
p->mutate(); // only const member function `operator->` is invoked on 
`p`
}
  }


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50883

Files:
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -606,6 +606,59 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.f()"));
 }
 
+TEST(ExprMutationAnalyzerTest, UniquePtr) {
+  const std::string UniquePtrDef =
+  "template  struct UniquePtr {"
+  "  UniquePtr();"
+  "  UniquePtr(const UniquePtr&) = delete;"
+  "  UniquePtr(UniquePtr&&);"
+  "  UniquePtr& operator=(const UniquePtr&) = delete;"
+  "  UniquePtr& operator=(UniquePtr&&);"
+  "  T& operator*() const;"
+  "  T* operator->() const;"
+  "};";
+
+  auto AST = tooling::buildASTFromCode(
+  UniquePtrDef + "void f() { UniquePtr x; *x = 10; }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("* x = 10"));
+
+  AST = tooling::buildASTFromCode(UniquePtrDef +
+  "void f() { UniquePtr x; *x; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(UniquePtrDef +
+  "void f() { UniquePtr x; *x; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(UniquePtrDef +
+  "struct S { int v; };"
+  "void f() { UniquePtr x; x->v; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
+
+  AST = tooling::buildASTFromCode(UniquePtrDef +
+  "struct S { int v; };"
+  "void f() { UniquePtr x; x->v; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(UniquePtrDef +
+  "struct S { void mf(); };"
+  "void f() { UniquePtr x; x->mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
+
+  AST = tooling::buildASTFromCode(
+  UniquePtrDef + "struct S { void mf() const; };"
+ "void f() { UniquePtr x; x->mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
 } // namespace test
 } // namespace tidy
 } // namespace clang
Index: clang-tidy/utils/ExprMutationAnalyzer.cpp
===
--- clang-tidy/utils/ExprMutationAnalyzer.cpp
+++ clang-tidy/utils/ExprMutationAnalyzer.cpp
@@ -51,6 +51,20 @@
   return referenceType(pointee(unless(isConstQualified(;
 };
 
+const auto nonConstPointerType = [] {
+  return pointerType(pointee(unless(isConstQualified(;
+};
+
+const auto isMoveOnly = [] {
+  return cxxRecordDecl(
+  hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted(,
+  hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted(,
+  unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
+unless(isDeleted(,
+   hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
+   unless(isDeleted()));
+};
+
 } // namespace
 
 const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
@@ -163,6 +177,15 @@
   const auto AsPointerFromArrayDecay =
   castExpr(hasCastKind(CK_ArrayToPointerDecay),
unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp)));
+  // Treat calling `operator->()` of move-only classes as taking address.
+  // These are typically smart pointers with unique ownership so we treat
+  // mutation of pointee as mutation of the smart pointer it

[PATCH] D50883: [clang-tidy] Handle unique owning smart pointers in ExprMutationAnalyzer

2018-08-17 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D50883#1203690, @JonasToth wrote:

> I am suprised that this does not automatically follow from the general rules. 
> At the end, smartpointers cant do anything else then 'normal' classes.
>
> The `operator+/->` were not handled before? The mutation of `SmartPtr x; 
> x->mf();` should already be catched, not?


Different from `std::vector::operator[]` which has two overloads for const and 
non-const access, `std::unique_ptr` only has one const version of `operator->`.
So for `SmartPtr x; x->mf();` we only see a const operator being invoked on 
`x`. `mf` is not a member of `SmartPtr` and the member call to `mf` is not on 
`x` directly, we never followed that far.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50883



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50953: [clang-tidy] Handle sugared reference types in ExprMutationAnalyzer

2018-08-19 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
shuaiwang added reviewers: aaron.ballman, alexfh.
Herald added subscribers: cfe-commits, Szelethus, a.sidorin, xazax.hun.
Herald added a reviewer: george.karpenkov.

This handles cases like this:

  typedef int& IntRef;
  void mutate(IntRef);
  void f() {
int x;
mutate(x);
  }

where the param type is a sugared type (`TypedefType`) instead of a
reference type directly.

Note that another category of similar but different cases are already
handled properly before:

  typedef int Int;
  void mutate(Int&);
  void f() {
int x;
mutate(x);
  }


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50953

Files:
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -168,6 +168,18 @@
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
 
+  AST = tooling::buildASTFromCode("typedef int& IntRef;"
+  "void g(IntRef); void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  struct identity { using type = T; };"
+  "template  void g(typename identity::type);"
+  "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
   AST = tooling::buildASTFromCode(
   "void f() { struct A {}; A operator+(A&, int); A x; x + 1; }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
@@ -348,13 +360,22 @@
 }
 
 TEST(ExprMutationAnalyzerTest, FollowRefModified) {
-  const auto AST = tooling::buildASTFromCode(
+  auto AST = tooling::buildASTFromCode(
   "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; "
   "int& r3 = r2; r3 = 10; }");
-  const auto Results =
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()),
   ElementsAre("r0", "r1", "r2", "r3", "r3 = 10"));
+
+  AST = tooling::buildASTFromCode(
+  "typedef int& IntRefX;"
+  "using IntRefY = int&;"
+  "void f() { int x; IntRefX r0 = x; IntRefY r1 = r0;"
+  "decltype((x)) r2 = r1; r2 = 10; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()),
+  ElementsAre("r0", "r1", "r2", "r2 = 10"));
 }
 
 TEST(ExprMutationAnalyzerTest, FollowRefNotModified) {
@@ -424,12 +445,19 @@
 }
 
 TEST(ExprMutationAnalyzerTest, CastToRefModified) {
-  const auto AST = tooling::buildASTFromCode(
+  auto AST = tooling::buildASTFromCode(
   "void f() { int x; static_cast(x) = 10; }");
-  const auto Results =
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()),
   ElementsAre("static_cast(x) = 10"));
+
+  AST = tooling::buildASTFromCode(
+  "typedef int& IntRef;"
+  "void f() { int x; static_cast(x) = 10; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()),
+  ElementsAre("static_cast(x) = 10"));
 }
 
 TEST(ExprMutationAnalyzerTest, CastToRefNotModified) {
@@ -483,11 +511,17 @@
 }
 
 TEST(ExprMutationAnalyzerTest, RangeForArrayByRefModified) {
-  const auto AST = tooling::buildASTFromCode(
+  auto AST = tooling::buildASTFromCode(
   "void f() { int x[2]; for (int& e : x) e = 10; }");
-  const auto Results =
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("e", "e = 10"));
+
+  AST = tooling::buildASTFromCode(
+  "typedef int& IntRef;"
+  "void f() { int x[2]; for (IntRef e : x) e = 10; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("e", "e = 10"));
 }
 
 TEST(ExprMutationAnalyzerTest, RangeForArrayByRefNotModified) {
Index: clang-tidy/utils/ExprMutationAnalyzer.cpp
===
--- clang-tidy/utils/ExprMutationAnalyzer.cpp
+++ clang-tidy/utils/ExprMutationAnalyzer.cpp
@@ -48,7 +48,8 @@
 }
 
 const auto nonConstReferenceType = [] {
-  return referenceType(pointee(unless(isConstQualified(;
+  return hasUnqualifiedDesugaredType(
+  referenceType(pointee(unless(isConstQualified();
 };
 
 } // namespace
___

[PATCH] D50617: [ASTMatchers] Let hasObjectExpression also support UnresolvedMemberExpr, CXXDependentScopeMemberExpr

2018-08-21 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 161830.
shuaiwang added a comment.

Fix issue with implicit access.


Repository:
  rC Clang

https://reviews.llvm.org/D50617

Files:
  docs/LibASTMatchersReference.html
  include/clang/ASTMatchers/ASTMatchers.h
  unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp

Index: unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1517,6 +1517,26 @@
 "struct X { int m; }; void f(X* x) { x->m; }",
 memberExpr(hasObjectExpression(
   hasType(pointsTo(recordDecl(hasName("X";
+  EXPECT_TRUE(matches("template  struct X { void f() { T t; t.m; } };",
+  cxxDependentScopeMemberExpr(hasObjectExpression(
+  declRefExpr(to(namedDecl(hasName("t";
+  EXPECT_TRUE(
+  matches("template  struct X { void f() { T t; t->m; } };",
+  cxxDependentScopeMemberExpr(hasObjectExpression(
+  declRefExpr(to(namedDecl(hasName("t";
+}
+
+TEST(HasObjectExpression, MatchesBaseOfMemberFunc) {
+  EXPECT_TRUE(matches(
+  "struct X { void f(); }; void g(X x) { x.f(); }",
+  memberExpr(hasObjectExpression(hasType(recordDecl(hasName("X")));
+  EXPECT_TRUE(matches("struct X { template  void f(); };"
+  "template  void g(X x) { x.f(); }",
+  unresolvedMemberExpr(hasObjectExpression(
+  hasType(recordDecl(hasName("X")));
+  EXPECT_TRUE(matches("template  void f(T t) { t.g(); }",
+  cxxDependentScopeMemberExpr(hasObjectExpression(
+  declRefExpr(to(namedDecl(hasName("t";
 }
 
 TEST(HasObjectExpression,
Index: include/clang/ASTMatchers/ASTMatchers.h
===
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -4806,8 +4806,17 @@
 ///   matches "x.m" and "m"
 /// with hasObjectExpression(...)
 ///   matching "x" and the implicit object expression of "m" which has type X*.
-AST_MATCHER_P(MemberExpr, hasObjectExpression,
-  internal::Matcher, InnerMatcher) {
+AST_POLYMORPHIC_MATCHER_P(
+hasObjectExpression,
+AST_POLYMORPHIC_SUPPORTED_TYPES(MemberExpr, UnresolvedMemberExpr,
+CXXDependentScopeMemberExpr),
+internal::Matcher, InnerMatcher) {
+  if (const auto *E = dyn_cast(&Node))
+if (E->isImplicitAccess())
+  return false;
+  if (const auto *E = dyn_cast(&Node))
+if (E->isImplicitAccess())
+  return false;
   return InnerMatcher.matches(*Node.getBase(), Finder, Builder);
 }
 
Index: docs/LibASTMatchersReference.html
===
--- docs/LibASTMatchersReference.html
+++ docs/LibASTMatchersReference.html
@@ -4668,6 +4668,20 @@
 
 
 
+MatcherCXXDependentScopeMemberExpr>hasObjectExpressionMatcherExpr> InnerMatcher
+Matches a member expression where the object expression is
+matched by a given matcher.
+
+Given
+  struct X { int m; };
+  void f(X x) { x.m; m; }
+memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))
+  matches "x.m" and "m"
+with hasObjectExpression(...)
+  matching "x" and the implicit object expression of "m" which has type X*.
+
+
+
 MatcherCXXForRangeStmt>hasBodyMatcherStmt> InnerMatcher
 Matches a 'for', 'while', 'do while' statement or a function
 definition that has a given body.
@@ -6692,6 +6706,20 @@
 
 
 
+MatcherUnresolvedMemberExpr>hasObjectExpressionMatcherExpr> InnerMatcher
+Matches a member expression where the object expression is
+matched by a given matcher.
+
+Given
+  struct X { int m; };
+  void f(X x) { x.m; m; }
+memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))
+  matches "x.m" and "m"
+with hasObjectExpression(...)
+  matching "x" and the implicit object expression of "m" which has type X*.
+
+
+
 MatcherUnresolvedUsingType>hasDeclarationconst MatcherDecl>  InnerMatcher
 Matches a node if the declaration associated with that node
 matches the given matcher.
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50619: [clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer

2018-08-21 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D50619#1207785, @JonasToth wrote:

> @shuaiwang Unfortunatly the analysis does not pass and fails on an assertion
>
>   → ~/opt/llvm/build/bin/clang-tidy 
> -checks=-*,cppcoreguidelines-const-correctness ItaniumDemangle.cpp --
>   clang-tidy: ../tools/clang/include/clang/AST/ExprCXX.h:3581: clang::Expr* 
> clang::CXXDependentScopeMemberExpr::getBase() const: Assertion 
> `!isImplicitAccess()' failed.
>   Abgebrochen (Speicherabzug geschrieben)
>
>
> I did not investigate further, sorry for the long time to try it out.


https://reviews.llvm.org/D50617 updated, could you help try again? Thanks!


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50619



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45702: [clang-tidy] Add a new check, readability-simplify-subscript-expr, that simplifies subscript expressions.

2018-05-13 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 146514.
shuaiwang marked 10 inline comments as done.
shuaiwang added a comment.

Rename to readability-simplify-subscript-expr and addressed other comments.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45702

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/readability/SimplifySubscriptExprCheck.cpp
  clang-tidy/readability/SimplifySubscriptExprCheck.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-simplify-subscript-expr.rst
  test/clang-tidy/readability-simplify-subscript-expr.cpp

Index: test/clang-tidy/readability-simplify-subscript-expr.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-simplify-subscript-expr.cpp
@@ -0,0 +1,108 @@
+// RUN: %check_clang_tidy %s readability-simplify-subscript-expr %t \
+// RUN: -config="{CheckOptions: \
+// RUN: [{key: readability-simplify-subscript-expr.Types, \
+// RUN:   value: '::std::basic_string;::std::basic_string_view;MyVector'}]}" --
+
+namespace std {
+
+template 
+class basic_string {
+ public:
+   using size_type = unsigned;
+   using value_type = T;
+   using reference = value_type&;
+   using const_reference = const value_type&;
+
+   reference operator[](size_type);
+   const_reference operator[](size_type) const;
+   T* data();
+   const T* data() const;
+};
+
+using string = basic_string;
+
+template 
+class basic_string_view {
+ public:
+  using size_type = unsigned;
+  using const_reference = const T&;
+  using const_pointer = const T*;
+
+  constexpr const_reference operator[](size_type) const;
+  constexpr const_pointer data() const noexcept;
+};
+
+using string_view = basic_string_view;
+
+}
+
+template 
+class MyVector {
+ public:
+  using size_type = unsigned;
+  using const_reference = const T&;
+  using const_pointer = const T*;
+
+  const_reference operator[](size_type) const;
+  const T* data() const noexcept;
+};
+
+#define DO(x) do { x; } while (false)
+#define ACCESS(x) (x)
+#define GET(x, i) (x).data()[i]
+
+template 
+class Foo {
+ public:
+  char bar(int i) {
+return x.data()[i];
+  }
+ private:
+  T x;
+};
+
+void f(int i) {
+  MyVector v;
+  int x = v.data()[i];
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: accessing an element of the container does not require a call to 'data()'; did you mean to use 'operator[]'? [readability-simplify-subscript-expr]
+  // CHECK-FIXES: int x = v[i];
+
+  std::string s;
+  char c1 = s.data()[i];
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: accessing an element
+  // CHECK-FIXES: char c1 = s[i];
+
+  std::string_view sv;
+  char c2 = sv.data()[i];
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: accessing an element
+  // CHECK-FIXES: char c2 = sv[i];
+
+  std::string* ps = &s;
+  char c3 = ps->data()[i];
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: accessing an element
+  // CHECK-FIXES: char c3 = (*ps)[i];
+
+  char c4 = (*ps).data()[i];
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: accessing an element
+  // CHECK-FIXES: char c4 = (*ps)[i];
+
+  DO(char c5 = s.data()[i]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: accessing an element
+  // CHECK-FIXES: DO(char c5 = s[i]);
+
+  char c6 = ACCESS(s).data()[i];
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: accessing an element
+  // CHECK-FIXES: char c6 = ACCESS(s)[i];
+
+  char c7 = ACCESS(s.data())[i];
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: accessing an element
+  // CHECK-FIXES: char c7 = ACCESS(s)[i];
+
+  char c8 = ACCESS(s.data()[i]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: accessing an element
+  // CHECK-FIXES: char c8 = ACCESS(s[i]);
+
+  char c9 = GET(s, i);
+
+  char c10 = Foo{}.bar(i);
+}
Index: docs/clang-tidy/checks/readability-simplify-subscript-expr.rst
===
--- /dev/null
+++ docs/clang-tidy/checks/readability-simplify-subscript-expr.rst
@@ -0,0 +1,23 @@
+.. title:: clang-tidy - readability-simplify-subscript-expr
+
+readability-simplify-subscript-expr
+===
+
+This check simplifies subscript expressions. Currently this covers calling
+``.data()`` and immediately doing an array subscript operation to obtain a
+single element, in which case simply calling ``operator[]`` suffice.
+
+Examples:
+
+.. code-block:: c++
+
+  std::string s = ...;
+  char c = s.data()[i];  // char c = s[i];
+
+Options
+---
+
+.. option:: Types
+
+   The list of type(s) that triggers this check. Default is
+   `::std::basic_string;::std::basic_string_view;::std::vector;::std::array`
Index: docs/clang-tidy/checks/list.rst
===
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -226,6 +226,7 @@
readability-redundant-string-cstr
readability-redundant-string-init
readability-simplify-boolean-expr
+   re

[PATCH] D45679: [clang-tidy] Add ExprMutationAnalyzer, that analyzes whether an expression is mutated within a statement.

2018-05-13 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 146531.
shuaiwang marked 3 inline comments as done.
shuaiwang added a comment.

Handle unevaluated expressions.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45679

Files:
  clang-tidy/utils/CMakeLists.txt
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  clang-tidy/utils/ExprMutationAnalyzer.h
  unittests/clang-tidy/CMakeLists.txt
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- /dev/null
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -0,0 +1,574 @@
+//===-- ExprMutationAnalyzerTest.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 "../clang-tidy/utils/ExprMutationAnalyzer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+using namespace clang::ast_matchers;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::ResultOf;
+using ::testing::StartsWith;
+using ::testing::Values;
+
+namespace {
+
+using ExprMatcher = internal::Matcher;
+using StmtMatcher = internal::Matcher;
+
+ExprMatcher declRefTo(StringRef Name) {
+  return declRefExpr(to(namedDecl(hasName(Name;
+}
+
+StmtMatcher withEnclosingCompound(ExprMatcher Matcher) {
+  return expr(Matcher, hasAncestor(compoundStmt().bind("stmt"))).bind("expr");
+}
+
+bool isMutated(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  const auto *const E = selectFirst("expr", Results);
+  return utils::ExprMutationAnalyzer(S, &AST->getASTContext()).isMutated(E);
+}
+
+SmallVector
+mutatedBy(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  SmallVector Chain;
+  utils::ExprMutationAnalyzer Analyzer(S, &AST->getASTContext());
+  for (const auto *E = selectFirst("expr", Results); E != nullptr;) {
+const Stmt *By = Analyzer.findMutation(E);
+std::string buffer;
+llvm::raw_string_ostream stream(buffer);
+By->printPretty(stream, nullptr, AST->getASTContext().getPrintingPolicy());
+Chain.push_back(StringRef(stream.str()).trim().str());
+E = dyn_cast(By);
+  }
+  return Chain;
+}
+
+std::string removeSpace(std::string s) {
+  s.erase(std::remove_if(s.begin(), s.end(),
+ [](char c) { return std::isspace(c); }),
+  s.end());
+  return s;
+}
+
+} // namespace
+
+TEST(ExprMutationAnalyzerTest, Trivial) {
+  const auto AST = tooling::buildASTFromCode("void f() { int x; x; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
+class AssignmentTest : public ::testing::TestWithParam {};
+
+TEST_P(AssignmentTest, AssignmentModifies) {
+  const std::string ModExpr = "x " + GetParam() + " 10";
+  const auto AST =
+  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllAssignmentOperators, AssignmentTest,
+Values("=", "+=", "-=", "*=", "/=", "%=", "&=", "|=",
+   "^=", "<<=", ">>="), );
+
+class IncDecTest : public ::testing::TestWithParam {};
+
+TEST_P(IncDecTest, IncDecModifies) {
+  const std::string ModExpr = GetParam();
+  const auto AST =
+  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllIncDecOperators, IncDecTest,
+Values("++x", "--x", "x++", "x--"), );
+
+TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) {
+  const auto AST = tooling::buildASTFromCode(
+  "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+}
+
+TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
+  const auto AST = tooling::buildASTFromCode(
+  "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
+TEST(ExprMutationA

[PATCH] D45702: [clang-tidy] Add a new check, readability-simplify-subscript-expr, that simplifies subscript expressions.

2018-05-13 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 146532.
shuaiwang marked 3 inline comments as done.
shuaiwang added a comment.

Addressed review comments.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45702

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ReadabilityTidyModule.cpp
  clang-tidy/readability/SimplifySubscriptExprCheck.cpp
  clang-tidy/readability/SimplifySubscriptExprCheck.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-simplify-subscript-expr.rst
  test/clang-tidy/readability-simplify-subscript-expr.cpp

Index: test/clang-tidy/readability-simplify-subscript-expr.cpp
===
--- /dev/null
+++ test/clang-tidy/readability-simplify-subscript-expr.cpp
@@ -0,0 +1,108 @@
+// RUN: %check_clang_tidy %s readability-simplify-subscript-expr %t \
+// RUN: -config="{CheckOptions: \
+// RUN: [{key: readability-simplify-subscript-expr.Types, \
+// RUN:   value: '::std::basic_string;::std::basic_string_view;MyVector'}]}" --
+
+namespace std {
+
+template 
+class basic_string {
+ public:
+   using size_type = unsigned;
+   using value_type = T;
+   using reference = value_type&;
+   using const_reference = const value_type&;
+
+   reference operator[](size_type);
+   const_reference operator[](size_type) const;
+   T* data();
+   const T* data() const;
+};
+
+using string = basic_string;
+
+template 
+class basic_string_view {
+ public:
+  using size_type = unsigned;
+  using const_reference = const T&;
+  using const_pointer = const T*;
+
+  constexpr const_reference operator[](size_type) const;
+  constexpr const_pointer data() const noexcept;
+};
+
+using string_view = basic_string_view;
+
+}
+
+template 
+class MyVector {
+ public:
+  using size_type = unsigned;
+  using const_reference = const T&;
+  using const_pointer = const T*;
+
+  const_reference operator[](size_type) const;
+  const T* data() const noexcept;
+};
+
+#define DO(x) do { x; } while (false)
+#define ACCESS(x) (x)
+#define GET(x, i) (x).data()[i]
+
+template 
+class Foo {
+ public:
+  char bar(int i) {
+return x.data()[i];
+  }
+ private:
+  T x;
+};
+
+void f(int i) {
+  MyVector v;
+  int x = v.data()[i];
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: accessing an element of the container does not require a call to 'data()'; did you mean to use 'operator[]'? [readability-simplify-subscript-expr]
+  // CHECK-FIXES: int x = v[i];
+
+  std::string s;
+  char c1 = s.data()[i];
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: accessing an element
+  // CHECK-FIXES: char c1 = s[i];
+
+  std::string_view sv;
+  char c2 = sv.data()[i];
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: accessing an element
+  // CHECK-FIXES: char c2 = sv[i];
+
+  std::string* ps = &s;
+  char c3 = ps->data()[i];
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: accessing an element
+  // CHECK-FIXES: char c3 = (*ps)[i];
+
+  char c4 = (*ps).data()[i];
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: accessing an element
+  // CHECK-FIXES: char c4 = (*ps)[i];
+
+  DO(char c5 = s.data()[i]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: accessing an element
+  // CHECK-FIXES: DO(char c5 = s[i]);
+
+  char c6 = ACCESS(s).data()[i];
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: accessing an element
+  // CHECK-FIXES: char c6 = ACCESS(s)[i];
+
+  char c7 = ACCESS(s.data())[i];
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: accessing an element
+  // CHECK-FIXES: char c7 = ACCESS(s)[i];
+
+  char c8 = ACCESS(s.data()[i]);
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: accessing an element
+  // CHECK-FIXES: char c8 = ACCESS(s[i]);
+
+  char c9 = GET(s, i);
+
+  char c10 = Foo{}.bar(i);
+}
Index: docs/clang-tidy/checks/readability-simplify-subscript-expr.rst
===
--- /dev/null
+++ docs/clang-tidy/checks/readability-simplify-subscript-expr.rst
@@ -0,0 +1,23 @@
+.. title:: clang-tidy - readability-simplify-subscript-expr
+
+readability-simplify-subscript-expr
+===
+
+This check simplifies subscript expressions. Currently this covers calling
+``.data()`` and immediately doing an array subscript operation to obtain a
+single element, in which case simply calling ``operator[]`` suffice.
+
+Examples:
+
+.. code-block:: c++
+
+  std::string s = ...;
+  char c = s.data()[i];  // char c = s[i];
+
+Options
+---
+
+.. option:: Types
+
+   The list of type(s) that triggers this check. Default is
+   `::std::basic_string;::std::basic_string_view;::std::vector;::std::array`
Index: docs/clang-tidy/checks/list.rst
===
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -226,6 +226,7 @@
readability-redundant-string-cstr
readability-redundant-string-init
readability-simplify-boolean-expr
+   readability-simplify-subscript-expr
readability-

[PATCH] D45702: [clang-tidy] Add a new check, readability-simplify-subscript-expr, that simplifies subscript expressions.

2018-05-16 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D45702#1101136, @aaron.ballman wrote:

> In https://reviews.llvm.org/D45702#1097294, @shuaiwang wrote:
>
> > Addressed review comments.
>
>
> This patch was approved; do you need someone to commit this for you?


Yes please :)


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45702



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50617: [ASTMatchers] Let hasObjectExpression also support UnresolvedMemberExpr, CXXDependentScopeMemberExpr

2018-08-23 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rC340547: [ASTMatchers] Let hasObjectExpression also support 
UnresolvedMemberExpr… (authored by shuaiwang, committed by ).

Changed prior to commit:
  https://reviews.llvm.org/D50617?vs=161830&id=162218#toc

Repository:
  rC Clang

https://reviews.llvm.org/D50617

Files:
  docs/LibASTMatchersReference.html
  include/clang/ASTMatchers/ASTMatchers.h
  unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp

Index: unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1517,6 +1517,26 @@
 "struct X { int m; }; void f(X* x) { x->m; }",
 memberExpr(hasObjectExpression(
   hasType(pointsTo(recordDecl(hasName("X";
+  EXPECT_TRUE(matches("template  struct X { void f() { T t; t.m; } };",
+  cxxDependentScopeMemberExpr(hasObjectExpression(
+  declRefExpr(to(namedDecl(hasName("t";
+  EXPECT_TRUE(
+  matches("template  struct X { void f() { T t; t->m; } };",
+  cxxDependentScopeMemberExpr(hasObjectExpression(
+  declRefExpr(to(namedDecl(hasName("t";
+}
+
+TEST(HasObjectExpression, MatchesBaseOfMemberFunc) {
+  EXPECT_TRUE(matches(
+  "struct X { void f(); }; void g(X x) { x.f(); }",
+  memberExpr(hasObjectExpression(hasType(recordDecl(hasName("X")));
+  EXPECT_TRUE(matches("struct X { template  void f(); };"
+  "template  void g(X x) { x.f(); }",
+  unresolvedMemberExpr(hasObjectExpression(
+  hasType(recordDecl(hasName("X")));
+  EXPECT_TRUE(matches("template  void f(T t) { t.g(); }",
+  cxxDependentScopeMemberExpr(hasObjectExpression(
+  declRefExpr(to(namedDecl(hasName("t";
 }
 
 TEST(HasObjectExpression,
Index: docs/LibASTMatchersReference.html
===
--- docs/LibASTMatchersReference.html
+++ docs/LibASTMatchersReference.html
@@ -4668,6 +4668,20 @@
 
 
 
+MatcherCXXDependentScopeMemberExpr>hasObjectExpressionMatcherExpr> InnerMatcher
+Matches a member expression where the object expression is
+matched by a given matcher.
+
+Given
+  struct X { int m; };
+  void f(X x) { x.m; m; }
+memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))
+  matches "x.m" and "m"
+with hasObjectExpression(...)
+  matching "x" and the implicit object expression of "m" which has type X*.
+
+
+
 MatcherCXXForRangeStmt>hasBodyMatcherStmt> InnerMatcher
 Matches a 'for', 'while', 'do while' statement or a function
 definition that has a given body.
@@ -6692,6 +6706,20 @@
 
 
 
+MatcherUnresolvedMemberExpr>hasObjectExpressionMatcherExpr> InnerMatcher
+Matches a member expression where the object expression is
+matched by a given matcher.
+
+Given
+  struct X { int m; };
+  void f(X x) { x.m; m; }
+memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))
+  matches "x.m" and "m"
+with hasObjectExpression(...)
+  matching "x" and the implicit object expression of "m" which has type X*.
+
+
+
 MatcherUnresolvedUsingType>hasDeclarationconst MatcherDecl>  InnerMatcher
 Matches a node if the declaration associated with that node
 matches the given matcher.
Index: include/clang/ASTMatchers/ASTMatchers.h
===
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -4825,8 +4825,17 @@
 ///   matches "x.m" and "m"
 /// with hasObjectExpression(...)
 ///   matching "x" and the implicit object expression of "m" which has type X*.
-AST_MATCHER_P(MemberExpr, hasObjectExpression,
-  internal::Matcher, InnerMatcher) {
+AST_POLYMORPHIC_MATCHER_P(
+hasObjectExpression,
+AST_POLYMORPHIC_SUPPORTED_TYPES(MemberExpr, UnresolvedMemberExpr,
+CXXDependentScopeMemberExpr),
+internal::Matcher, InnerMatcher) {
+  if (const auto *E = dyn_cast(&Node))
+if (E->isImplicitAccess())
+  return false;
+  if (const auto *E = dyn_cast(&Node))
+if (E->isImplicitAccess())
+  return false;
   return InnerMatcher.matches(*Node.getBase(), Finder, Builder);
 }
 
___
cfe

[PATCH] D50619: [clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer

2018-09-09 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

Ping :)


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50619



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45679: [clang-tidy] Add ExprMutationAnalyzer, that analyzes whether an expression is mutated within a statement.

2018-09-10 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D45679#1183116, @george.karpenkov wrote:

> @aaron.ballman @alexfh @shuaiwang Would it be possible to move that code into 
> a matcher, or into a something which could be used from Clang? We would also 
> like to use similar functionality, but can't include stuff from clang-tidy.


FYI I haven't forgot about this, I'll look into doing it after a few pending 
changes are in.




Comment at: clang-tidy/utils/ExprMutationAnalyzer.h:38
+  const Stmt *findDeclMutation(ArrayRef Matches);
+  const Stmt *findDeclMutation(const Decl *Dec);
+

lebedev.ri wrote:
> lebedev.ri wrote:
> > lebedev.ri wrote:
> > > @shuaiwang, @JonasToth hi.
> > > Is it very intentional that this `findDeclMutation()` is private?
> > > 
> > > Is there some other way i should be doing if i have a statement `S`,
> > > a declRefExpr `D`, and i want to find the statement `Q`, which mutates
> > > the underlying variable to which the declRefExpr `D` refers?
> > (the statement `Q` within the statement `S`, of course)
> @shuaiwang after a disscussion about this in IRC with @JonasToth, i have 
> filed https://bugs.llvm.org/show_bug.cgi?id=3
> But i'm failing to CC you there. Are you not registered in the bugzilla?
There's no real reason findDeclMutation is private other than that there wasn't 
a use case :)
Feel free to make it public if you find it useful that way.

I'll take a look at the bug, thanks for reporting it!
(I don't have an account there yet, I'm requesting one right now, will follow 
up in the bug)


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45679



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45679: [clang-tidy] Add ExprMutationAnalyzer, that analyzes whether an expression is mutated within a statement.

2018-09-10 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

I have a rough idea about how `findPointeeMutation` would look like, I'm pretty 
sure I can use a lot of your help :)
My current plan:

- Finish the few existing pending changes
- Move the analyzer to `clang/lib/Analysis` (likely remove 
`PseudoConstantAnalysis` there as well since it appears to be dead code for 
years)
- Implement `findPointeeMutation`

In https://reviews.llvm.org/D45679#1229071, @JonasToth wrote:

> @shuaiwang What are you working on next? Do you have an idea on how we
>  will handle the pointee of a pointer? That would be very interesting for
>  my const-correctness check. if you need help with anything, just say so.
>  I would like to support
>
> Am 10.09.2018 um 18:49 schrieb Shuai Wang via Phabricator:
>
> > shuaiwang added a comment.
> > 
> > In https://reviews.llvm.org/D45679#1183116, @george.karpenkov wrote:
> > 
> >> @aaron.ballman @alexfh @shuaiwang Would it be possible to move that code 
> >> into a matcher, or into a something which could be used from Clang? We 
> >> would also like to use similar functionality, but can't include stuff from 
> >> clang-tidy.
> > 
> > FYI I haven't forgot about this, I'll look into doing it after a few 
> > pending changes are in.
> > 
> > 
> >  Comment at: clang-tidy/utils/ExprMutationAnalyzer.h:38
> >  +  const Stmt *findDeclMutation(ArrayRef 
> > Matches);
> >  +  const Stmt *findDeclMutation(const Decl *Dec);
> >  +
> > 
> >  
> > 
> > lebedev.ri wrote:
> > 
> >> lebedev.ri wrote:
> >> 
> >>> lebedev.ri wrote:
> >>> 
>  @shuaiwang, @JonasToth hi.
>   Is it very intentional that this `findDeclMutation()` is private?
>  
>  Is there some other way i should be doing if i have a statement `S`,
>   a declRefExpr `D`, and i want to find the statement `Q`, which mutates
>   the underlying variable to which the declRefExpr `D` refers?
> >>> 
> >>> (the statement `Q` within the statement `S`, of course)
> >> 
> >> @shuaiwang after a disscussion about this in IRC with @JonasToth, i have 
> >> filed https://bugs.llvm.org/show_bug.cgi?id=3
> >>  But i'm failing to CC you there. Are you not registered in the bugzilla?
> > 
> > There's no real reason findDeclMutation is private other than that there 
> > wasn't a use case :)
> >  Feel free to make it public if you find it useful that way.
> > 
> > I'll take a look at the bug, thanks for reporting it!
> >  (I don't have an account there yet, I'm requesting one right now, will 
> > follow up in the bug)
> > 
> > Repository:
> > 
> >   rCTE Clang Tools Extra
> > 
> > https://reviews.llvm.org/D45679





Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45679



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50619: [clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer

2018-09-10 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rCTE341848: [clang-tidy] Handle unresolved expressions in 
ExprMutationAnalyzer (authored by shuaiwang, committed by ).

Changed prior to commit:
  https://reviews.llvm.org/D50619?vs=160960&id=164704#toc

Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50619

Files:
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -114,6 +114,26 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
 }
 
+TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) {
+  auto AST = tooling::buildASTFromCode(
+  "struct X { template  void mf(); };"
+  "template  void f() { X x; x.mf(); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+
+  AST =
+  tooling::buildASTFromCode("template  void f() { T x; x.mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+
+  AST = tooling::buildASTFromCode(
+  "template  struct X;"
+  "template  void f() { X x; x.mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+}
+
 TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
   const auto AST = tooling::buildASTFromCode(
   "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
@@ -292,6 +312,51 @@
   ElementsAre("std::forward(x)"));
 }
 
+TEST(ExprMutationAnalyzerTest, CallUnresolved) {
+  auto AST =
+  tooling::buildASTFromCode("template  void f() { T x; g(x); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f() { char x[N]; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f(T t) { int x; g(t, x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(t, x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f(T t) { int x; t.mf(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("t.mf(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  struct S;"
+  "template  void f() { S s; int x; s.mf(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "struct S { template  void mf(); };"
+  "template  void f(S s) { int x; s.mf(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)"));
+
+  AST = tooling::buildASTFromCode("template "
+  "void g(F f) { int x; f(x); } ");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  void f() { int x; (void)T(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)"));
+}
+
 TEST(ExprMutationAnalyzerTest, ReturnAsValue) {
   const auto AST = tooling::buildASTFromCode("int f() { int x; return x; }");
   const auto Results =
@@ -347,6 +412,21 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
 }
 
+TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) {
+  const auto AST = tooling::buildASTFromCode(
+  "template  struct S { static constexpr int v = 8; };"
+  "template <> struct S { static constexpr int v = 4; };"
+  "void g(char*);"
+  "template  void f() { char x[S::v]; g(x); }"
+  "template <> void f() { char y[S::v]; g(y); }");
+  const auto ResultsX =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(ResultsX, AST.get()), ElementsAre("g(x)"));
+  const auto ResultsY =
+  match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(ResultsY, AST.get()), ElementsAre("y"));
+}
+
 TEST(ExprMutationAnalyzerTest, FollowRefModifie

[PATCH] D51884: [clang-tidy] ExprMutationAnalyzer: construct from references. Fixes PR38888

2018-09-10 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang accepted this revision.
shuaiwang added a comment.
This revision is now accepted and ready to land.

LGTM :)


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D51884



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50883: [clang-tidy] Handle unique owning smart pointers in ExprMutationAnalyzer

2018-09-10 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D50883#1229297, @JonasToth wrote:

> >   Different from std::vector::operator[] which has two overloads for const 
> > and non-const access, std::unique_ptr only has one const version of 
> > `operator->`.
> > 
> > So for SmartPtr x; x->mf(); we only see a const operator being invoked on 
> > x. mf is not a member of SmartPtr and the member call to mf is not on x 
> > directly, we never followed that far.
>
> I think the `operator->` is transitively called, isn't it? (see andrei 
> alexandrescu talk here: https://youtu.be/ozOgzlxIsdg?t=15m55s where he gives 
> a nice class for generic locking)
>  Maybe there is something more general at work? I think @aaron.ballman knows 
> more about deeper language questions :)


Right, for `x->mf()` (where `x` is some kind of smart pointer), `operator->` is 
invoked on the smart pointer itself, and then that yields a (real) pointer, in 
which case the `operator->` magically reappears and is applied on the real 
pointer.
For now we're basically treating `SmartPtr::operator->()` the same as "taking 
the address of `Exp`", which is treated as immediately mutating `Exp`. The 
benefit of this approach is: when we're able to do `findPointeeMutation`, we'll 
be able to further follow the pointer and see whether the pointee is mutated, 
and we'll be able to distinguish between these two cases:

  struct Foo {
  void mf();
  void cmf() const;
  };
  std::unique_ptr p;
  p->mf(); // mutates Foo
  p->cmf(); // doesn't mutate Foo, but is treated as mutation for now. Can be 
improved when we're able to do findPointeeMutation( () call on p> )


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50883



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D51898: Revert "[clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer"

2018-09-10 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
Herald added subscribers: cfe-commits, Szelethus, a.sidorin, xazax.hun.
Herald added a reviewer: george.karpenkov.

Tests somehow break on windows (and only on windows)
http://lab.llvm.org:8011/builders/clang-x64-ninja-win7/builds/13003
http://lab.llvm.org:8011/builders/clang-x86-windows-msvc2015/builds/13747

I have yet figure out why so reverting to unbreak first.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D51898

Files:
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -114,26 +114,6 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
 }
 
-TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) {
-  auto AST = tooling::buildASTFromCode(
-  "struct X { template  void mf(); };"
-  "template  void f() { X x; x.mf(); }");
-  auto Results =
-  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
-
-  AST =
-  tooling::buildASTFromCode("template  void f() { T x; x.mf(); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
-
-  AST = tooling::buildASTFromCode(
-  "template  struct X;"
-  "template  void f() { X x; x.mf(); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
-}
-
 TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
   const auto AST = tooling::buildASTFromCode(
   "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
@@ -312,51 +292,6 @@
   ElementsAre("std::forward(x)"));
 }
 
-TEST(ExprMutationAnalyzerTest, CallUnresolved) {
-  auto AST =
-  tooling::buildASTFromCode("template  void f() { T x; g(x); }");
-  auto Results =
-  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  void f() { char x[N]; g(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  void f(T t) { int x; g(t, x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(t, x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  void f(T t) { int x; t.mf(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("t.mf(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  struct S;"
-  "template  void f() { S s; int x; s.mf(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "struct S { template  void mf(); };"
-  "template  void f(S s) { int x; s.mf(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)"));
-
-  AST = tooling::buildASTFromCode("template "
-  "void g(F f) { int x; f(x); } ");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  void f() { int x; (void)T(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)"));
-}
-
 TEST(ExprMutationAnalyzerTest, ReturnAsValue) {
   const auto AST = tooling::buildASTFromCode("int f() { int x; return x; }");
   const auto Results =
@@ -412,21 +347,6 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
 }
 
-TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) {
-  const auto AST = tooling::buildASTFromCode(
-  "template  struct S { static constexpr int v = 8; };"
-  "template <> struct S { static constexpr int v = 4; };"
-  "void g(char*);"
-  "template  void f() { char x[S::v]; g(x); }"
-  "template <> void f() { char y[S::v]; g(y); }");
-  const auto ResultsX =
-  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(ResultsX, AST.get()), ElementsAre("g(x)"));
-  const auto ResultsY =
-  match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
-  EXPECT_T

[PATCH] D51898: Revert "[clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer"

2018-09-10 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 164773.
shuaiwang added a comment.

rebase


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D51898

Files:
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -114,26 +114,6 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
 }
 
-TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) {
-  auto AST = tooling::buildASTFromCode(
-  "struct X { template  void mf(); };"
-  "template  void f() { X x; x.mf(); }");
-  auto Results =
-  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
-
-  AST =
-  tooling::buildASTFromCode("template  void f() { T x; x.mf(); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
-
-  AST = tooling::buildASTFromCode(
-  "template  struct X;"
-  "template  void f() { X x; x.mf(); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
-}
-
 TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
   const auto AST = tooling::buildASTFromCode(
   "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
@@ -312,51 +292,6 @@
   ElementsAre("std::forward(x)"));
 }
 
-TEST(ExprMutationAnalyzerTest, CallUnresolved) {
-  auto AST =
-  tooling::buildASTFromCode("template  void f() { T x; g(x); }");
-  auto Results =
-  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  void f() { char x[N]; g(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  void f(T t) { int x; g(t, x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(t, x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  void f(T t) { int x; t.mf(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("t.mf(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  struct S;"
-  "template  void f() { S s; int x; s.mf(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "struct S { template  void mf(); };"
-  "template  void f(S s) { int x; s.mf(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)"));
-
-  AST = tooling::buildASTFromCode("template "
-  "void g(F f) { int x; f(x); } ");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  void f() { int x; (void)T(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)"));
-}
-
 TEST(ExprMutationAnalyzerTest, ReturnAsValue) {
   const auto AST = tooling::buildASTFromCode("int f() { int x; return x; }");
   const auto Results =
@@ -412,21 +347,6 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
 }
 
-TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) {
-  const auto AST = tooling::buildASTFromCode(
-  "template  struct S { static constexpr int v = 8; };"
-  "template <> struct S { static constexpr int v = 4; };"
-  "void g(char*);"
-  "template  void f() { char x[S::v]; g(x); }"
-  "template <> void f() { char y[S::v]; g(y); }");
-  const auto ResultsX =
-  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(ResultsX, AST.get()), ElementsAre("g(x)"));
-  const auto ResultsY =
-  match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(ResultsY, AST.get()), ElementsAre("y"));
-}
-
 TEST(ExprMutationAnalyzerTest, FollowRefModified) {
   const auto AST = tooling::buildASTFromCode(
   "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; "
@@ -478,43 +398,21 @@
 }
 
 TEST(ExprMutationAnalyzerTest, NestedMemberModified) {
-  auto

[PATCH] D51898: Revert "[clang-tidy] Handle unresolved expressions in ExprMutationAnalyzer"

2018-09-10 Thread Shuai Wang via Phabricator via cfe-commits
This revision was not accepted when it landed; it landed in state "Needs 
Review".
This revision was automatically updated to reflect the committed changes.
Closed by commit rL341886: Revert "[clang-tidy] Handle unresolved 
expressions in ExprMutationAnalyzer" (authored by shuaiwang, committed by 
).
Herald added a subscriber: llvm-commits.

Repository:
  rL LLVM

https://reviews.llvm.org/D51898

Files:
  clang-tools-extra/trunk/clang-tidy/utils/ExprMutationAnalyzer.cpp
  clang-tools-extra/trunk/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: clang-tools-extra/trunk/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- clang-tools-extra/trunk/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ clang-tools-extra/trunk/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -114,26 +114,6 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
 }
 
-TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) {
-  auto AST = tooling::buildASTFromCode(
-  "struct X { template  void mf(); };"
-  "template  void f() { X x; x.mf(); }");
-  auto Results =
-  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
-
-  AST =
-  tooling::buildASTFromCode("template  void f() { T x; x.mf(); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
-
-  AST = tooling::buildASTFromCode(
-  "template  struct X;"
-  "template  void f() { X x; x.mf(); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
-}
-
 TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
   const auto AST = tooling::buildASTFromCode(
   "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
@@ -312,51 +292,6 @@
   ElementsAre("std::forward(x)"));
 }
 
-TEST(ExprMutationAnalyzerTest, CallUnresolved) {
-  auto AST =
-  tooling::buildASTFromCode("template  void f() { T x; g(x); }");
-  auto Results =
-  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  void f() { char x[N]; g(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  void f(T t) { int x; g(t, x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(t, x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  void f(T t) { int x; t.mf(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("t.mf(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  struct S;"
-  "template  void f() { S s; int x; s.mf(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "struct S { template  void mf(); };"
-  "template  void f(S s) { int x; s.mf(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)"));
-
-  AST = tooling::buildASTFromCode("template "
-  "void g(F f) { int x; f(x); } ");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)"));
-
-  AST = tooling::buildASTFromCode(
-  "template  void f() { int x; (void)T(x); }");
-  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)"));
-}
-
 TEST(ExprMutationAnalyzerTest, ReturnAsValue) {
   const auto AST = tooling::buildASTFromCode("int f() { int x; return x; }");
   const auto Results =
@@ -412,21 +347,6 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
 }
 
-TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) {
-  const auto AST = tooling::buildASTFromCode(
-  "template  struct S { static constexpr int v = 8; };"
-  "template <> struct S { static constexpr int v = 4; };"
-  "void g(char*);"
-  "template  void f() { char x[S::v]; g(x); }"
-  "template <> void f() { char y[S::v]; g(y); }");
-  const auto ResultsX =
-  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(ResultsX, AST.get()), ElementsAre("g(x)"));
-  const auto ResultsY =
-  match(withEnclosingCompound(declRe

[PATCH] D50953: [clang-tidy] Handle sugared reference types in ExprMutationAnalyzer

2018-09-10 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 164806.
shuaiwang marked 2 inline comments as done.
shuaiwang added a comment.

more test cases.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50953

Files:
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -191,6 +191,24 @@
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
 
+  AST = tooling::buildASTFromCode("typedef int& IntRef;"
+  "void g(IntRef); void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST =
+  tooling::buildASTFromCode("template  using TRef = T&;"
+"void g(TRef); void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  struct identity { using type = T; };"
+  "template  void g(typename identity::type);"
+  "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
   AST = tooling::buildASTFromCode(
   "void f() { struct A {}; A operator+(A&, int); A x; x + 1; }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
@@ -214,6 +232,25 @@
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
 
+  AST = tooling::buildASTFromCode("typedef const int& CIntRef;"
+  "void g(CIntRef); void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "template  using CTRef = const T&;"
+  "void g(CTRef); void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "template  struct identity { using type = T; };"
+  "template "
+  "void g(typename identity::type);"
+  "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
   AST = tooling::buildASTFromCode(
   "void f() { struct A {}; A operator+(const A&, int); A x; x + 1; }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
@@ -440,22 +477,44 @@
 }
 
 TEST(ExprMutationAnalyzerTest, FollowRefModified) {
-  const auto AST = tooling::buildASTFromCode(
+  auto AST = tooling::buildASTFromCode(
   "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; "
   "int& r3 = r2; r3 = 10; }");
-  const auto Results =
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()),
   ElementsAre("r0", "r1", "r2", "r3", "r3 = 10"));
+
+  AST = tooling::buildASTFromCode(
+  "typedef int& IntRefX;"
+  "using IntRefY = int&;"
+  "void f() { int x; IntRefX r0 = x; IntRefY r1 = r0;"
+  "decltype((x)) r2 = r1; r2 = 10; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()),
+  ElementsAre("r0", "r1", "r2", "r2 = 10"));
 }
 
 TEST(ExprMutationAnalyzerTest, FollowRefNotModified) {
-  const auto AST = tooling::buildASTFromCode(
+  auto AST = tooling::buildASTFromCode(
   "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; "
   "int& r3 = r2; int& r4 = r3; int& r5 = r4;}");
-  const auto Results =
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "void f() { int x; int& r0 = x; const int& r1 = r0;}");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "typedef const int& CIntRefX;"
+  "using CIntRefY = const int&;"
+  "void f() { int x; int& r0 = x; CIntRefX r1 = r0;"
+  "CIntRefY r2 = r1; decltype((r1)) r3 = r2;}");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
 TEST(ExprMutationAnalyzerTest, FollowConditionalRefModifie

[PATCH] D50953: [clang-tidy] Handle sugared reference types in ExprMutationAnalyzer

2018-09-10 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D50953#1229287, @JonasToth wrote:

> What happens to pointers in a typedef (in the sense of `*` instead of `&`)?


I checked around and I believe reference type is the only type we're explicitly 
matching right now. We'll need to handle carefully when handling pointer types 
in the future.




Comment at: clang-tidy/utils/ExprMutationAnalyzer.cpp:51
 const auto nonConstReferenceType = [] {
-  return referenceType(pointee(unless(isConstQualified(;
+  return hasUnqualifiedDesugaredType(
+  referenceType(pointee(unless(isConstQualified();

JonasToth wrote:
> Not directly related, but this matcher matches universal references, even 
> though they might result in a value. (one of the bugs lebedevri reported).
Noted, I'll take a look at the bug.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50953



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50883: [clang-tidy] Handle unique owning smart pointers in ExprMutationAnalyzer

2018-09-10 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 164810.
shuaiwang marked an inline comment as done.
shuaiwang added a comment.

rebase & add test case


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50883

Files:
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -724,6 +724,65 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.f()"));
 }
 
+TEST(ExprMutationAnalyzerTest, UniquePtr) {
+  const std::string UniquePtrDef =
+  "template  struct UniquePtr {"
+  "  UniquePtr();"
+  "  UniquePtr(const UniquePtr&) = delete;"
+  "  UniquePtr(UniquePtr&&);"
+  "  UniquePtr& operator=(const UniquePtr&) = delete;"
+  "  UniquePtr& operator=(UniquePtr&&);"
+  "  T& operator*() const;"
+  "  T* operator->() const;"
+  "};";
+
+  auto AST = tooling::buildASTFromCode(
+  UniquePtrDef + "void f() { UniquePtr x; *x = 10; }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("* x = 10"));
+
+  AST = tooling::buildASTFromCode(UniquePtrDef +
+  "void f() { UniquePtr x; *x; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(UniquePtrDef +
+  "void f() { UniquePtr x; *x; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(UniquePtrDef +
+  "struct S { int v; };"
+  "void f() { UniquePtr x; x->v; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
+
+  AST = tooling::buildASTFromCode(UniquePtrDef +
+  "struct S { int v; };"
+  "void f() { UniquePtr x; x->v; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(UniquePtrDef +
+  "struct S { void mf(); };"
+  "void f() { UniquePtr x; x->mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
+
+  AST = tooling::buildASTFromCode(
+  UniquePtrDef + "struct S { void mf() const; };"
+ "void f() { UniquePtr x; x->mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCodeWithArgs(
+  UniquePtrDef + "template  void f() { UniquePtr x; x->mf(); }",
+  {"-fno-delayed-template-parsing"});
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x->mf()"));
+}
+
 } // namespace test
 } // namespace tidy
 } // namespace clang
Index: clang-tidy/utils/ExprMutationAnalyzer.cpp
===
--- clang-tidy/utils/ExprMutationAnalyzer.cpp
+++ clang-tidy/utils/ExprMutationAnalyzer.cpp
@@ -51,6 +51,20 @@
   return referenceType(pointee(unless(isConstQualified(;
 };
 
+const auto nonConstPointerType = [] {
+  return pointerType(pointee(unless(isConstQualified(;
+};
+
+const auto isMoveOnly = [] {
+  return cxxRecordDecl(
+  hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted(,
+  hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted(,
+  unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
+unless(isDeleted(,
+   hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
+   unless(isDeleted()));
+};
+
 } // namespace
 
 const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
@@ -168,6 +182,15 @@
   const auto AsPointerFromArrayDecay =
   castExpr(hasCastKind(CK_ArrayToPointerDecay),
unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp)));
+  // Treat calling `operator->()` of move-only classes as taking address.
+  // These are typically smart pointers with unique ownership so we treat
+  // mutation of pointee as mutation of the smart pointer itself.
+  const auto AsOperatorArrowThis = cxxOperatorCallExpr(
+  hasOverloadedOperatorName("->"),
+  callee(cxxMethodDecl(
+

[PATCH] D50883: [clang-tidy] Handle unique owning smart pointers in ExprMutationAnalyzer

2018-09-10 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added inline comments.



Comment at: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp:658
+ "void f() { UniquePtr x; x->mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));

JonasToth wrote:
> Template testcases i miss:
> 
> ```
> // modifying
> template 
> void f() { UnqiuePtr x; x->mf(); }
> 
> // constant
> template 
> void f2() { UnqiuePtr x; x->cmf(); }
> 
> // indecidable for the template itself, but only the instantiations
> template 
> void f3() { T x; x->cmf(); }
> 
> struct const_class { void cmf() const; }
> struct modifying_class { void cmf(); };
> 
> void call_template() {
> // don't trigger
> f3>();
> // trigger modification
> f3();
> }
> 
> // again not decidable by the template itself
> template 
> void f4() { T t; *t; }
> 
> struct very_weird {
> int& operator*() { return *new int(42); }
> };
> void call_template_deref() {
>   // no modification
>   f4();
>   // modification, because deref is not const
>   f4>():
> }
> ```
Added a case with template. However I don't think we can do much whenever 
template appears: only the AST of **uninstantiated** template worth analyzing, 
because whatever analyze result (diag or fixit) would have to be applied to the 
template itself not the instantiations.
So the fact that the template argument of `f3` or `f4` could happen to be 
`UniquePtr` doesn't really matter when we analyze `f3` and `f4`, we have to 
analyze the way assuming the "worst" anyway.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50883



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50883: [clang-tidy] Handle unique owning smart pointers in ExprMutationAnalyzer

2018-09-11 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added inline comments.



Comment at: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp:658
+ "void f() { UniquePtr x; x->mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));

JonasToth wrote:
> shuaiwang wrote:
> > JonasToth wrote:
> > > Template testcases i miss:
> > > 
> > > ```
> > > // modifying
> > > template 
> > > void f() { UnqiuePtr x; x->mf(); }
> > > 
> > > // constant
> > > template 
> > > void f2() { UnqiuePtr x; x->cmf(); }
> > > 
> > > // indecidable for the template itself, but only the instantiations
> > > template 
> > > void f3() { T x; x->cmf(); }
> > > 
> > > struct const_class { void cmf() const; }
> > > struct modifying_class { void cmf(); };
> > > 
> > > void call_template() {
> > > // don't trigger
> > > f3>();
> > > // trigger modification
> > > f3();
> > > }
> > > 
> > > // again not decidable by the template itself
> > > template 
> > > void f4() { T t; *t; }
> > > 
> > > struct very_weird {
> > > int& operator*() { return *new int(42); }
> > > };
> > > void call_template_deref() {
> > >   // no modification
> > >   f4();
> > >   // modification, because deref is not const
> > >   f4>():
> > > }
> > > ```
> > Added a case with template. However I don't think we can do much whenever 
> > template appears: only the AST of **uninstantiated** template worth 
> > analyzing, because whatever analyze result (diag or fixit) would have to be 
> > applied to the template itself not the instantiations.
> > So the fact that the template argument of `f3` or `f4` could happen to be 
> > `UniquePtr` doesn't really matter when we analyze `f3` and `f4`, we have to 
> > analyze the way assuming the "worst" anyway.
> Yes, whenever there is something type-dependent we have to bail, thats why i 
> would like to see the testcases to show that we actually bail, even when one 
> instantiation would not modify.
> 
> I believe we do analyze all versions of the templated functions 
> (uninstantiated and every instantiation), don't we? With that there can be 
> conflicting results.
I see, it's the conflicting results you're going after :)
Good news is that we actually don't analyze all versions, we only analyze the 
version (instantiated or not) corresponding to the "scope" stmt passed into the 
constructor. Semantic-wise I feel this makes sense because if we're given an 
instantiated version we shouldn't bail out because nothing is type-dependent 
anymore in the instantiated version.
Also I think conflicts won't happen much in practice, most (all?) checks 
naturally pass in the uninstantiated version, in order to pass in an 
instantiated version a check needs to:
- Find an instantiation point
- Match and extract the function decl from the callExpr
- Extract function body compontStmt from the function decl
at that point the check owner likely knows pretty well what they're doing and 
shouldn't be surprised that analyze results conflicts if they happen to also 
analyze an uninstantiated version.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50883



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50883: [clang-tidy] Handle unique owning smart pointers in ExprMutationAnalyzer

2018-09-11 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

Yeah let's see what happens in the wild and decide whether we need further 
actions. In any case I think that deserves a separate diff.
Is there other concerns about this diff?

In https://reviews.llvm.org/D50883#1230805, @JonasToth wrote:

> Your point is valid, that the decision of what to analyze should be done
>  outside. My const-correctness check does analyze all versions of the
>  templated function, because it just matches on
>  `functionDecl(compoundStmt())`.
>
> Maybe we just need some experience with real world code. The
>  const-correctness thing is close to finish in its first version. Then we
>  can exercise its results.
>
> Am 11.09.2018 um 18:15 schrieb Shuai Wang via Phabricator:
>
> > I see, it's the conflicting results you're going after :)
> >  Good news is that we actually don't analyze all versions, we only analyze 
> > the version (instantiated or not) corresponding to the "scope" stmt passed 
> > into the constructor. Semantic-wise I feel this makes sense because if 
> > we're given an instantiated version we shouldn't bail out because nothing 
> > is type-dependent anymore in the instantiated version.
> >  Also I think conflicts won't happen much in practice, most (all?) checks 
> > naturally pass in the uninstantiated version, in order to pass in an 
> > instantiated version a check needs to:
> > 
> > - Find an instantiation point
> > - Match and extract the function decl from the callExpr
> > - Extract function body compontStmt from the function decl at that point 
> > the check owner likely knows pretty well what they're doing and shouldn't 
> > be surprised that analyze results conflicts if they happen to also analyze 
> > an uninstantiated version.
> > 
> > Repository:
> > 
> >   rCTE Clang Tools Extra
> > 
> > https://reviews.llvm.org/D50883





Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50883



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50883: [clang-tidy] Handle unique owning smart pointers in ExprMutationAnalyzer

2018-09-11 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rCTE341967: [clang-tidy] Handle unique owning smart pointers 
in ExprMutationAnalyzer (authored by shuaiwang, committed by ).

Changed prior to commit:
  https://reviews.llvm.org/D50883?vs=164810&id=164926#toc

Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50883

Files:
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: clang-tidy/utils/ExprMutationAnalyzer.cpp
===
--- clang-tidy/utils/ExprMutationAnalyzer.cpp
+++ clang-tidy/utils/ExprMutationAnalyzer.cpp
@@ -51,6 +51,20 @@
   return referenceType(pointee(unless(isConstQualified(;
 };
 
+const auto nonConstPointerType = [] {
+  return pointerType(pointee(unless(isConstQualified(;
+};
+
+const auto isMoveOnly = [] {
+  return cxxRecordDecl(
+  hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted(,
+  hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted(,
+  unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
+unless(isDeleted(,
+   hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
+   unless(isDeleted()));
+};
+
 } // namespace
 
 const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
@@ -168,6 +182,15 @@
   const auto AsPointerFromArrayDecay =
   castExpr(hasCastKind(CK_ArrayToPointerDecay),
unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp)));
+  // Treat calling `operator->()` of move-only classes as taking address.
+  // These are typically smart pointers with unique ownership so we treat
+  // mutation of pointee as mutation of the smart pointer itself.
+  const auto AsOperatorArrowThis = cxxOperatorCallExpr(
+  hasOverloadedOperatorName("->"),
+  callee(cxxMethodDecl(
+  ofClass(isMoveOnly()),
+  returns(hasUnqualifiedDesugaredType(nonConstPointerType(),
+  argumentCountIs(1), hasArgument(0, equalsNode(Exp)));
 
   // Used as non-const-ref argument when calling a function.
   // An argument is assumed to be non-const-ref when the function is unresolved.
@@ -197,8 +220,8 @@
   const auto Matches =
   match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
AsAmpersandOperand, AsPointerFromArrayDecay,
-   AsNonConstRefArg, AsLambdaRefCaptureInit,
-   AsNonConstRefReturn))
+   AsOperatorArrowThis, AsNonConstRefArg,
+   AsLambdaRefCaptureInit, AsNonConstRefReturn))
 .bind("stmt")),
 Stm, Context);
   return selectFirst("stmt", Matches);
@@ -250,6 +273,21 @@
 }
 
 const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) {
+  // Follow non-const reference returned by `operator*()` of move-only classes.
+  // These are typically smart pointers with unique ownership so we treat
+  // mutation of pointee as mutation of the smart pointer itself.
+  const auto Ref = match(
+  findAll(cxxOperatorCallExpr(
+  hasOverloadedOperatorName("*"),
+  callee(cxxMethodDecl(ofClass(isMoveOnly()),
+   returns(hasUnqualifiedDesugaredType(
+   nonConstReferenceType(),
+  argumentCountIs(1), hasArgument(0, equalsNode(Exp)))
+  .bind("expr")),
+  Stm, Context);
+  if (const Stmt *S = findExprMutation(Ref))
+return S;
+
   // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
   const auto Refs = match(
   stmt(forEachDescendant(
Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -724,6 +724,65 @@
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.f()"));
 }
 
+TEST(ExprMutationAnalyzerTest, UniquePtr) {
+  const std::string UniquePtrDef =
+  "template  struct UniquePtr {"
+  "  UniquePtr();"
+  "  UniquePtr(const UniquePtr&) = delete;"
+  "  UniquePtr(UniquePtr&&);"
+  "  UniquePtr& operator=(const UniquePtr&) = delete;"
+  "  UniquePtr& operator=(UniquePtr&&);"
+  "  T& operator*() const;"
+  "  T* operator->() const;"
+  "};";
+
+  auto AST = tooling::buildASTFromCode(
+  UniquePtrDef + "void f() { UniquePtr x; *x = 10; }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("* x = 10"));
+
+  AST = tooling::buildASTFromCode(UniquePtrDef +
+  "void f

[PATCH] D50953: [clang-tidy] Handle sugared reference types in ExprMutationAnalyzer

2018-09-11 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 164929.
shuaiwang added a comment.

more test cases


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50953

Files:
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -168,6 +168,15 @@
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
 
+  AST = tooling::buildASTFromCode("void g(int*); void f() { int* x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode("typedef int* IntPtr;"
+  "void g(IntPtr); void f() { int* x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
   AST = tooling::buildASTFromCode(
   "void f() { struct A {}; A operator+(A, int); A x; x + 1; }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
@@ -191,6 +200,24 @@
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
 
+  AST = tooling::buildASTFromCode("typedef int& IntRef;"
+  "void g(IntRef); void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST =
+  tooling::buildASTFromCode("template  using TRef = T&;"
+"void g(TRef); void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  struct identity { using type = T; };"
+  "template  void g(typename identity::type);"
+  "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
   AST = tooling::buildASTFromCode(
   "void f() { struct A {}; A operator+(A&, int); A x; x + 1; }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
@@ -214,6 +241,25 @@
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
 
+  AST = tooling::buildASTFromCode("typedef const int& CIntRef;"
+  "void g(CIntRef); void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "template  using CTRef = const T&;"
+  "void g(CTRef); void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "template  struct identity { using type = T; };"
+  "template "
+  "void g(typename identity::type);"
+  "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
   AST = tooling::buildASTFromCode(
   "void f() { struct A {}; A operator+(const A&, int); A x; x + 1; }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
@@ -369,10 +415,19 @@
 }
 
 TEST(ExprMutationAnalyzerTest, ReturnAsValue) {
-  const auto AST = tooling::buildASTFromCode("int f() { int x; return x; }");
-  const auto Results =
+  auto AST = tooling::buildASTFromCode("int f() { int x; return x; }");
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode("int* f() { int* x; return x; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode("typedef int* IntPtr;"
+  "IntPtr f() { int* x; return x; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
 TEST(ExprMutationAnalyzerTest, ReturnAsNonConstRef) {
@@ -440,22 +495,44 @@
 }
 
 TEST(ExprMutationAnalyzerTest, FollowRefModified) {
-  const auto AST = tooling::buildASTFromCode(
+  auto AST = tooling::buildASTFromCode(
   "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; "
   "int& r3 = r2; r3 = 10; }");
-  const auto Resu

[PATCH] D50953: [clang-tidy] Handle sugared reference types in ExprMutationAnalyzer

2018-09-11 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D50953#1230138, @JonasToth wrote:

> In https://reviews.llvm.org/D50953#1230003, @shuaiwang wrote:
>
> > In https://reviews.llvm.org/D50953#1229287, @JonasToth wrote:
> >
> > > What happens to pointers in a typedef (in the sense of `*` instead of 
> > > `&`)?
> >
> >
> > I checked around and I believe reference type is the only type we're 
> > explicitly matching right now. We'll need to handle carefully when handling 
> > pointer types in the future.
>
>
> We match on pointers as values. So figure this out `int * >const< ptr = 
> nullptr`.
>  And the constness transformation is especially intersting for pointer 
> typedefs, because in `typedef int * IntPtr; const IntPtr p1; IntPtr const 
> p2;` p1 and p2 are  different things. It would be nice if you could check, 
> that the value semantic of the pointer is detected through the typedef as 
> well.


Added test cases verifying we're treating pointers as values.
I feel constness doesn't matter much since we're treating them as values and 
both const values & non-const values are just values.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50953



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50953: [clang-tidy] Handle sugared reference types in ExprMutationAnalyzer

2018-09-11 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 164938.
shuaiwang added a comment.

More test cases:

- Mutating pointers
- Const values


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50953

Files:
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -168,6 +168,15 @@
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
 
+  AST = tooling::buildASTFromCode("void g(int*); void f() { int* x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode("typedef int* IntPtr;"
+  "void g(IntPtr); void f() { int* x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
   AST = tooling::buildASTFromCode(
   "void f() { struct A {}; A operator+(A, int); A x; x + 1; }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
@@ -184,13 +193,77 @@
   EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
+TEST(ExprMutationAnalyzerTest, ByConstValueArgument) {
+  auto AST =
+  tooling::buildASTFromCode("void g(const int); void f() { int x; g(x); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "void g(int* const); void f() { int* x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST =
+  tooling::buildASTFromCode("typedef int* const CIntPtr;"
+"void g(CIntPtr); void f() { int* x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "void f() { struct A {}; A operator+(const A, int); A x; x + 1; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "void f() { struct A { A(const int); }; int x; A y(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "void f() { struct A { A(); A(const A); }; A x; A y(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
 TEST(ExprMutationAnalyzerTest, ByNonConstRefArgument) {
   auto AST =
   tooling::buildASTFromCode("void g(int&); void f() { int x; g(x); }");
   auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
 
+  AST = tooling::buildASTFromCode("typedef int& IntRef;"
+  "void g(IntRef); void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST =
+  tooling::buildASTFromCode("template  using TRef = T&;"
+"void g(TRef); void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "template  struct identity { using type = T; };"
+  "template  void g(typename identity::type);"
+  "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST =
+  tooling::buildASTFromCode("typedef int* IntPtr;"
+"void g(IntPtr&); void f() { int* x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "typedef int* IntPtr; typedef IntPtr& IntPtrRef;"
+  "void g(IntPtrRef); void f() { int* x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
   AST = tooling::buildASTFromCode(
   "void f() { struct A {}; A operator+(A&, int); A x; x + 1; }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
@@ -214,6 +287,25 @@
   match

[PATCH] D50953: [clang-tidy] Handle sugared reference types in ExprMutationAnalyzer

2018-09-11 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL341986: [clang-tidy] Handle sugared reference types in 
ExprMutationAnalyzer (authored by shuaiwang, committed by ).
Herald added a subscriber: llvm-commits.

Repository:
  rL LLVM

https://reviews.llvm.org/D50953

Files:
  clang-tools-extra/trunk/clang-tidy/utils/ExprMutationAnalyzer.cpp
  clang-tools-extra/trunk/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: clang-tools-extra/trunk/clang-tidy/utils/ExprMutationAnalyzer.cpp
===
--- clang-tools-extra/trunk/clang-tidy/utils/ExprMutationAnalyzer.cpp
+++ clang-tools-extra/trunk/clang-tidy/utils/ExprMutationAnalyzer.cpp
@@ -48,11 +48,13 @@
 }
 
 const auto nonConstReferenceType = [] {
-  return referenceType(pointee(unless(isConstQualified(;
+  return hasUnqualifiedDesugaredType(
+  referenceType(pointee(unless(isConstQualified();
 };
 
 const auto nonConstPointerType = [] {
-  return pointerType(pointee(unless(isConstQualified(;
+  return hasUnqualifiedDesugaredType(
+  pointerType(pointee(unless(isConstQualified();
 };
 
 const auto isMoveOnly = [] {
@@ -185,12 +187,11 @@
   // Treat calling `operator->()` of move-only classes as taking address.
   // These are typically smart pointers with unique ownership so we treat
   // mutation of pointee as mutation of the smart pointer itself.
-  const auto AsOperatorArrowThis = cxxOperatorCallExpr(
-  hasOverloadedOperatorName("->"),
-  callee(cxxMethodDecl(
-  ofClass(isMoveOnly()),
-  returns(hasUnqualifiedDesugaredType(nonConstPointerType(),
-  argumentCountIs(1), hasArgument(0, equalsNode(Exp)));
+  const auto AsOperatorArrowThis =
+  cxxOperatorCallExpr(hasOverloadedOperatorName("->"),
+  callee(cxxMethodDecl(ofClass(isMoveOnly()),
+   returns(nonConstPointerType(,
+  argumentCountIs(1), hasArgument(0, equalsNode(Exp)));
 
   // Used as non-const-ref argument when calling a function.
   // An argument is assumed to be non-const-ref when the function is unresolved.
Index: clang-tools-extra/trunk/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- clang-tools-extra/trunk/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ clang-tools-extra/trunk/unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -168,6 +168,15 @@
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
 
+  AST = tooling::buildASTFromCode("void g(int*); void f() { int* x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode("typedef int* IntPtr;"
+  "void g(IntPtr); void f() { int* x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
   AST = tooling::buildASTFromCode(
   "void f() { struct A {}; A operator+(A, int); A x; x + 1; }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
@@ -184,13 +193,77 @@
   EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
+TEST(ExprMutationAnalyzerTest, ByConstValueArgument) {
+  auto AST =
+  tooling::buildASTFromCode("void g(const int); void f() { int x; g(x); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "void g(int* const); void f() { int* x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST =
+  tooling::buildASTFromCode("typedef int* const CIntPtr;"
+"void g(CIntPtr); void f() { int* x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "void f() { struct A {}; A operator+(const A, int); A x; x + 1; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "void f() { struct A { A(const int); }; int x; A y(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "void f() { struct A { A(); A(const A); }; A x; A y(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
 TEST(ExprMutationAnalyzerTest, ByNonConstRefArgument) {
   auto AST 

[PATCH] D51946: Remove PseudoConstantAnalysis

2018-09-11 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
Herald added subscribers: cfe-commits, mgorny.

It's not used anywhere for years. The last usage is removed in 
https://reviews.llvm.org/rL198476 in 2014.


Repository:
  rC Clang

https://reviews.llvm.org/D51946

Files:
  include/clang/Analysis/Analyses/PseudoConstantAnalysis.h
  include/clang/Analysis/AnalysisDeclContext.h
  lib/Analysis/AnalysisDeclContext.cpp
  lib/Analysis/CMakeLists.txt
  lib/Analysis/PseudoConstantAnalysis.cpp

Index: lib/Analysis/PseudoConstantAnalysis.cpp
===
--- lib/Analysis/PseudoConstantAnalysis.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-//== PseudoConstantAnalysis.cpp - Find Pseudoconstants in the AST-*- C++ -*-==//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===--===//
-//
-// This file tracks the usage of variables in a Decl body to see if they are
-// never written to, implying that they constant. This is useful in static
-// analysis to see if a developer might have intended a variable to be const.
-//
-//===--===//
-
-#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/Expr.h"
-#include "clang/AST/Stmt.h"
-#include "llvm/ADT/SmallPtrSet.h"
-#include 
-
-using namespace clang;
-
-typedef llvm::SmallPtrSet VarDeclSet;
-
-PseudoConstantAnalysis::PseudoConstantAnalysis(const Stmt *DeclBody) :
-  DeclBody(DeclBody), Analyzed(false) {
-  NonConstantsImpl = new VarDeclSet;
-  UsedVarsImpl = new VarDeclSet;
-}
-
-PseudoConstantAnalysis::~PseudoConstantAnalysis() {
-  delete (VarDeclSet*)NonConstantsImpl;
-  delete (VarDeclSet*)UsedVarsImpl;
-}
-
-// Returns true if the given ValueDecl is never written to in the given DeclBody
-bool PseudoConstantAnalysis::isPseudoConstant(const VarDecl *VD) {
-  // Only local and static variables can be pseudoconstants
-  if (!VD->hasLocalStorage() && !VD->isStaticLocal())
-return false;
-
-  if (!Analyzed) {
-RunAnalysis();
-Analyzed = true;
-  }
-
-  VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl;
-
-  return !NonConstants->count(VD);
-}
-
-// Returns true if the variable was used (self assignments don't count)
-bool PseudoConstantAnalysis::wasReferenced(const VarDecl *VD) {
-  if (!Analyzed) {
-RunAnalysis();
-Analyzed = true;
-  }
-
-  VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl;
-
-  return UsedVars->count(VD);
-}
-
-// Returns a Decl from a (Block)DeclRefExpr (if any)
-const Decl *PseudoConstantAnalysis::getDecl(const Expr *E) {
-  if (const DeclRefExpr *DR = dyn_cast(E))
-return DR->getDecl();
-  else
-return nullptr;
-}
-
-void PseudoConstantAnalysis::RunAnalysis() {
-  std::deque WorkList;
-  VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl;
-  VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl;
-
-  // Start with the top level statement of the function
-  WorkList.push_back(DeclBody);
-
-  while (!WorkList.empty()) {
-const Stmt *Head = WorkList.front();
-WorkList.pop_front();
-
-if (const Expr *Ex = dyn_cast(Head))
-  Head = Ex->IgnoreParenCasts();
-
-switch (Head->getStmtClass()) {
-// Case 1: Assignment operators modifying VarDecls
-case Stmt::BinaryOperatorClass: {
-  const BinaryOperator *BO = cast(Head);
-  // Look for a Decl on the LHS
-  const Decl *LHSDecl = getDecl(BO->getLHS()->IgnoreParenCasts());
-  if (!LHSDecl)
-break;
-
-  // We found a binary operator with a DeclRefExpr on the LHS. We now check
-  // for any of the assignment operators, implying that this Decl is being
-  // written to.
-  switch (BO->getOpcode()) {
-  // Self-assignments don't count as use of a variable
-  case BO_Assign: {
-// Look for a DeclRef on the RHS
-const Decl *RHSDecl = getDecl(BO->getRHS()->IgnoreParenCasts());
-
-// If the Decls match, we have self-assignment
-if (LHSDecl == RHSDecl)
-  // Do not visit the children
-  continue;
-
-LLVM_FALLTHROUGH;
-  }
-  case BO_AddAssign:
-  case BO_SubAssign:
-  case BO_MulAssign:
-  case BO_DivAssign:
-  case BO_AndAssign:
-  case BO_OrAssign:
-  case BO_XorAssign:
-  case BO_ShlAssign:
-  case BO_ShrAssign: {
-const VarDecl *VD = dyn_cast(LHSDecl);
-// The DeclRefExpr is being assigned to - mark it as non-constant
-if (VD)
-  NonConstants->insert(VD);
-break;
-  }
-
-  default:
-break;
-  }
-  break;
-}
-
-// Case 2: Pre/post increment/decrement and address of
-case Stmt::UnaryOperatorClass: {
-  const UnaryOperator *UO = cast(Head);
-
-  // Look for a DeclRef in the subexpress

[PATCH] D51948: [analyzer] Add ExprMutationAnalyzer

2018-09-11 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
shuaiwang added a reviewer: george.karpenkov.
Herald added subscribers: cfe-commits, Szelethus, mikhail.ramalho, a.sidorin, 
szepet, xazax.hun, mgorny.

This is 1/2 of moving ExprMutationAnalyzer from clangtidy to
clang/Analysis.
This diff along simply copies the ExprMutationAnalyzer over with trivial
modifications (e.g. include path, namespace)
2/2 will migrate existing usage of ExprMutationAnalyzer and remove the
original copy inside clangtidy.


Repository:
  rC Clang

https://reviews.llvm.org/D51948

Files:
  include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
  lib/Analysis/CMakeLists.txt
  lib/Analysis/ExprMutationAnalyzer.cpp
  unittests/Analysis/CMakeLists.txt
  unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: unittests/Analysis/ExprMutationAnalyzerTest.cpp
===
--- /dev/null
+++ unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -0,0 +1,943 @@
+//===-- ExprMutationAnalyzerTest.cpp --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include 
+
+namespace clang {
+
+using namespace clang::ast_matchers;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::ResultOf;
+using ::testing::StartsWith;
+using ::testing::Values;
+
+namespace {
+
+using ExprMatcher = internal::Matcher;
+using StmtMatcher = internal::Matcher;
+
+ExprMatcher declRefTo(StringRef Name) {
+  return declRefExpr(to(namedDecl(hasName(Name;
+}
+
+StmtMatcher withEnclosingCompound(ExprMatcher Matcher) {
+  return expr(Matcher, hasAncestor(compoundStmt().bind("stmt"))).bind("expr");
+}
+
+bool isMutated(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  const auto *const E = selectFirst("expr", Results);
+  return ExprMutationAnalyzer(*S, AST->getASTContext()).isMutated(E);
+}
+
+SmallVector
+mutatedBy(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  SmallVector Chain;
+  ExprMutationAnalyzer Analyzer(*S, AST->getASTContext());
+  for (const auto *E = selectFirst("expr", Results); E != nullptr;) {
+const Stmt *By = Analyzer.findMutation(E);
+std::string buffer;
+llvm::raw_string_ostream stream(buffer);
+By->printPretty(stream, nullptr, AST->getASTContext().getPrintingPolicy());
+Chain.push_back(StringRef(stream.str()).trim().str());
+E = dyn_cast(By);
+  }
+  return Chain;
+}
+
+std::string removeSpace(std::string s) {
+  s.erase(std::remove_if(s.begin(), s.end(),
+ [](char c) { return std::isspace(c); }),
+  s.end());
+  return s;
+}
+
+} // namespace
+
+TEST(ExprMutationAnalyzerTest, Trivial) {
+  const auto AST = tooling::buildASTFromCode("void f() { int x; x; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
+class AssignmentTest : public ::testing::TestWithParam {};
+
+TEST_P(AssignmentTest, AssignmentModifies) {
+  const std::string ModExpr = "x " + GetParam() + " 10";
+  const auto AST =
+  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllAssignmentOperators, AssignmentTest,
+Values("=", "+=", "-=", "*=", "/=", "%=", "&=", "|=",
+   "^=", "<<=", ">>="), );
+
+class IncDecTest : public ::testing::TestWithParam {};
+
+TEST_P(IncDecTest, IncDecModifies) {
+  const std::string ModExpr = GetParam();
+  const auto AST =
+  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllIncDecOperators, IncDecTest,
+Values("++x", "--x", "x++", "x--"), );
+
+TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) {
+  const auto AST = tooling::buildASTFromCode(
+  "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+}
+
+TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) 

[PATCH] D51948: [analyzer] Add ExprMutationAnalyzer

2018-09-11 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL341994: [analyzer] Add ExprMutationAnalyzer (authored by 
shuaiwang, committed by ).
Herald added a subscriber: llvm-commits.

Repository:
  rL LLVM

https://reviews.llvm.org/D51948

Files:
  cfe/trunk/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
  cfe/trunk/lib/Analysis/CMakeLists.txt
  cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp
  cfe/trunk/unittests/Analysis/CMakeLists.txt
  cfe/trunk/unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp
===
--- cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp
+++ cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -0,0 +1,308 @@
+//===-- ExprMutationAnalyzer.cpp --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace clang {
+using namespace ast_matchers;
+
+namespace {
+
+AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
+  return llvm::is_contained(Node.capture_inits(), E);
+}
+
+AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
+  ast_matchers::internal::Matcher, InnerMatcher) {
+  const DeclStmt *const Range = Node.getRangeStmt();
+  return InnerMatcher.matches(*Range, Finder, Builder);
+}
+
+const ast_matchers::internal::VariadicDynCastAllOfMatcher
+cxxTypeidExpr;
+
+AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
+  return Node.isPotentiallyEvaluated();
+}
+
+const ast_matchers::internal::VariadicDynCastAllOfMatcher
+cxxNoexceptExpr;
+
+const ast_matchers::internal::VariadicDynCastAllOfMatcher
+genericSelectionExpr;
+
+AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
+  ast_matchers::internal::Matcher, InnerMatcher) {
+  return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
+}
+
+const auto nonConstReferenceType = [] {
+  return hasUnqualifiedDesugaredType(
+  referenceType(pointee(unless(isConstQualified();
+};
+
+const auto nonConstPointerType = [] {
+  return hasUnqualifiedDesugaredType(
+  pointerType(pointee(unless(isConstQualified();
+};
+
+const auto isMoveOnly = [] {
+  return cxxRecordDecl(
+  hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted(,
+  hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted(,
+  unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
+unless(isDeleted(,
+   hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
+   unless(isDeleted()));
+};
+
+} // namespace
+
+const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
+  const auto Memoized = Results.find(Exp);
+  if (Memoized != Results.end())
+return Memoized->second;
+
+  if (isUnevaluated(Exp))
+return Results[Exp] = nullptr;
+
+  for (const auto &Finder : {&ExprMutationAnalyzer::findDirectMutation,
+ &ExprMutationAnalyzer::findMemberMutation,
+ &ExprMutationAnalyzer::findArrayElementMutation,
+ &ExprMutationAnalyzer::findCastMutation,
+ &ExprMutationAnalyzer::findRangeLoopMutation,
+ &ExprMutationAnalyzer::findReferenceMutation}) {
+if (const Stmt *S = (this->*Finder)(Exp))
+  return Results[Exp] = S;
+  }
+
+  return Results[Exp] = nullptr;
+}
+
+bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
+  return selectFirst(
+ "expr",
+ match(
+ findAll(
+ expr(equalsNode(Exp),
+  anyOf(
+  // `Exp` is part of the underlying expression of
+  // decltype/typeof if it has an ancestor of
+  // typeLoc.
+  hasAncestor(typeLoc(unless(
+  hasAncestor(unaryExprOrTypeTraitExpr(),
+  hasAncestor(expr(anyOf(
+  // `UnaryExprOrTypeTraitExpr` is unevaluated
+  // unless it's sizeof on VLA.
+  unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
+  hasArgumentOfType(variableArrayType(),
+  // `CXXTypeidExpr` is unevaluated unless it's
+  // applied to an expression of glvalue of
+

[PATCH] D51948: [analyzer] Add ExprMutationAnalyzer

2018-09-11 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rC341994: [analyzer] Add ExprMutationAnalyzer (authored by 
shuaiwang, committed by ).

Changed prior to commit:
  https://reviews.llvm.org/D51948?vs=164969&id=164972#toc

Repository:
  rL LLVM

https://reviews.llvm.org/D51948

Files:
  include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
  lib/Analysis/CMakeLists.txt
  lib/Analysis/ExprMutationAnalyzer.cpp
  unittests/Analysis/CMakeLists.txt
  unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: lib/Analysis/ExprMutationAnalyzer.cpp
===
--- lib/Analysis/ExprMutationAnalyzer.cpp
+++ lib/Analysis/ExprMutationAnalyzer.cpp
@@ -0,0 +1,308 @@
+//===-- ExprMutationAnalyzer.cpp --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace clang {
+using namespace ast_matchers;
+
+namespace {
+
+AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
+  return llvm::is_contained(Node.capture_inits(), E);
+}
+
+AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
+  ast_matchers::internal::Matcher, InnerMatcher) {
+  const DeclStmt *const Range = Node.getRangeStmt();
+  return InnerMatcher.matches(*Range, Finder, Builder);
+}
+
+const ast_matchers::internal::VariadicDynCastAllOfMatcher
+cxxTypeidExpr;
+
+AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
+  return Node.isPotentiallyEvaluated();
+}
+
+const ast_matchers::internal::VariadicDynCastAllOfMatcher
+cxxNoexceptExpr;
+
+const ast_matchers::internal::VariadicDynCastAllOfMatcher
+genericSelectionExpr;
+
+AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
+  ast_matchers::internal::Matcher, InnerMatcher) {
+  return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
+}
+
+const auto nonConstReferenceType = [] {
+  return hasUnqualifiedDesugaredType(
+  referenceType(pointee(unless(isConstQualified();
+};
+
+const auto nonConstPointerType = [] {
+  return hasUnqualifiedDesugaredType(
+  pointerType(pointee(unless(isConstQualified();
+};
+
+const auto isMoveOnly = [] {
+  return cxxRecordDecl(
+  hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted(,
+  hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted(,
+  unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
+unless(isDeleted(,
+   hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
+   unless(isDeleted()));
+};
+
+} // namespace
+
+const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
+  const auto Memoized = Results.find(Exp);
+  if (Memoized != Results.end())
+return Memoized->second;
+
+  if (isUnevaluated(Exp))
+return Results[Exp] = nullptr;
+
+  for (const auto &Finder : {&ExprMutationAnalyzer::findDirectMutation,
+ &ExprMutationAnalyzer::findMemberMutation,
+ &ExprMutationAnalyzer::findArrayElementMutation,
+ &ExprMutationAnalyzer::findCastMutation,
+ &ExprMutationAnalyzer::findRangeLoopMutation,
+ &ExprMutationAnalyzer::findReferenceMutation}) {
+if (const Stmt *S = (this->*Finder)(Exp))
+  return Results[Exp] = S;
+  }
+
+  return Results[Exp] = nullptr;
+}
+
+bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
+  return selectFirst(
+ "expr",
+ match(
+ findAll(
+ expr(equalsNode(Exp),
+  anyOf(
+  // `Exp` is part of the underlying expression of
+  // decltype/typeof if it has an ancestor of
+  // typeLoc.
+  hasAncestor(typeLoc(unless(
+  hasAncestor(unaryExprOrTypeTraitExpr(),
+  hasAncestor(expr(anyOf(
+  // `UnaryExprOrTypeTraitExpr` is unevaluated
+  // unless it's sizeof on VLA.
+  unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
+  hasArgumentOfType(variableArrayType(),
+  // `CXXTypeidExpr` is unevaluated unless it's
+  // applied to an expression of glvalue of
+  // polymorphic class type.
+   

[PATCH] D51950: [clangtidy] Remove old copy of ExprMutationAnalyzer

2018-09-11 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
shuaiwang added reviewers: george.karpenkov, JonasToth.
Herald added subscribers: cfe-commits, Szelethus, a.sidorin, mgorny.

This is 2/2 of moving ExprMutationAnalyzer from clangtidy to clang/Analysis.
ExprMutationAnalyzer is moved to clang/Analysis in 
https://reviews.llvm.org/D51948.
This diff migrates existing usages within clangtidy to point to the new
location and remove the old copy of ExprMutationAnalyzer.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D51950

Files:
  clang-tidy/performance/ForRangeCopyCheck.cpp
  clang-tidy/performance/UnnecessaryValueParamCheck.cpp
  clang-tidy/utils/CMakeLists.txt
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  clang-tidy/utils/ExprMutationAnalyzer.h
  unittests/clang-tidy/CMakeLists.txt
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
+++ /dev/null
@@ -1,947 +0,0 @@
-//===-- ExprMutationAnalyzerTest.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 "../clang-tidy/utils/ExprMutationAnalyzer.h"
-#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include "clang/ASTMatchers/ASTMatchers.h"
-#include "clang/Tooling/Tooling.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include 
-
-namespace clang {
-namespace tidy {
-namespace test {
-
-using namespace clang::ast_matchers;
-using ::testing::ElementsAre;
-using ::testing::IsEmpty;
-using ::testing::ResultOf;
-using ::testing::StartsWith;
-using ::testing::Values;
-
-namespace {
-
-using ExprMatcher = internal::Matcher;
-using StmtMatcher = internal::Matcher;
-
-ExprMatcher declRefTo(StringRef Name) {
-  return declRefExpr(to(namedDecl(hasName(Name;
-}
-
-StmtMatcher withEnclosingCompound(ExprMatcher Matcher) {
-  return expr(Matcher, hasAncestor(compoundStmt().bind("stmt"))).bind("expr");
-}
-
-bool isMutated(const SmallVectorImpl &Results, ASTUnit *AST) {
-  const auto *const S = selectFirst("stmt", Results);
-  const auto *const E = selectFirst("expr", Results);
-  return utils::ExprMutationAnalyzer(*S, AST->getASTContext()).isMutated(E);
-}
-
-SmallVector
-mutatedBy(const SmallVectorImpl &Results, ASTUnit *AST) {
-  const auto *const S = selectFirst("stmt", Results);
-  SmallVector Chain;
-  utils::ExprMutationAnalyzer Analyzer(*S, AST->getASTContext());
-  for (const auto *E = selectFirst("expr", Results); E != nullptr;) {
-const Stmt *By = Analyzer.findMutation(E);
-std::string buffer;
-llvm::raw_string_ostream stream(buffer);
-By->printPretty(stream, nullptr, AST->getASTContext().getPrintingPolicy());
-Chain.push_back(StringRef(stream.str()).trim().str());
-E = dyn_cast(By);
-  }
-  return Chain;
-}
-
-std::string removeSpace(std::string s) {
-  s.erase(std::remove_if(s.begin(), s.end(),
- [](char c) { return std::isspace(c); }),
-  s.end());
-  return s;
-}
-
-} // namespace
-
-TEST(ExprMutationAnalyzerTest, Trivial) {
-  const auto AST = tooling::buildASTFromCode("void f() { int x; x; }");
-  const auto Results =
-  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_FALSE(isMutated(Results, AST.get()));
-}
-
-class AssignmentTest : public ::testing::TestWithParam {};
-
-TEST_P(AssignmentTest, AssignmentModifies) {
-  const std::string ModExpr = "x " + GetParam() + " 10";
-  const auto AST =
-  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
-  const auto Results =
-  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
-}
-
-INSTANTIATE_TEST_CASE_P(AllAssignmentOperators, AssignmentTest,
-Values("=", "+=", "-=", "*=", "/=", "%=", "&=", "|=",
-   "^=", "<<=", ">>="), );
-
-class IncDecTest : public ::testing::TestWithParam {};
-
-TEST_P(IncDecTest, IncDecModifies) {
-  const std::string ModExpr = GetParam();
-  const auto AST =
-  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
-  const auto Results =
-  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
-}
-
-INSTANTIATE_TEST_CASE_P(AllIncDecOperators, IncDecTest,
-Values("++x", "--x", "x++", "x--"), );
-
-TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) {
-  const auto AST = tooling::buildASTFromCode(
-  "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }");
-  const auto Results =
-  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  

[PATCH] D51950: [clangtidy] Remove old copy of ExprMutationAnalyzer

2018-09-11 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rCTE342006: [clangtidy] Remove old copy of 
ExprMutationAnalyzer (authored by shuaiwang, committed by ).

Changed prior to commit:
  https://reviews.llvm.org/D51950?vs=164977&id=164991#toc

Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D51950

Files:
  clang-tidy/performance/ForRangeCopyCheck.cpp
  clang-tidy/performance/UnnecessaryValueParamCheck.cpp
  clang-tidy/utils/CMakeLists.txt
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  clang-tidy/utils/ExprMutationAnalyzer.h
  unittests/clang-tidy/CMakeLists.txt
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: clang-tidy/performance/ForRangeCopyCheck.cpp
===
--- clang-tidy/performance/ForRangeCopyCheck.cpp
+++ clang-tidy/performance/ForRangeCopyCheck.cpp
@@ -9,9 +9,9 @@
 
 #include "ForRangeCopyCheck.h"
 #include "../utils/DeclRefExprUtils.h"
-#include "../utils/ExprMutationAnalyzer.h"
 #include "../utils/FixItHintUtils.h"
 #include "../utils/TypeTraits.h"
+#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
 
 using namespace clang::ast_matchers;
 
@@ -88,8 +88,7 @@
   // Because the fix (changing to `const auto &`) will introduce an unused
   // compiler warning which can't be suppressed.
   // Since this case is very rare, it is safe to ignore it.
-  if (!utils::ExprMutationAnalyzer(*ForRange.getBody(), Context)
-   .isMutated(&LoopVar) &&
+  if (!ExprMutationAnalyzer(*ForRange.getBody(), Context).isMutated(&LoopVar) &&
   !utils::decl_ref_expr::allDeclRefExprs(LoopVar, *ForRange.getBody(),
  Context)
.empty()) {
Index: clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===
--- clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -10,10 +10,10 @@
 #include "UnnecessaryValueParamCheck.h"
 
 #include "../utils/DeclRefExprUtils.h"
-#include "../utils/ExprMutationAnalyzer.h"
 #include "../utils/FixItHintUtils.h"
 #include "../utils/Matchers.h"
 #include "../utils/TypeTraits.h"
+#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Lex/Lexer.h"
 #include "clang/Lex/Preprocessor.h"
@@ -95,14 +95,14 @@
   // Do not trigger on non-const value parameters when they are mutated either
   // within the function body or within init expression(s) when the function is
   // a ctor.
-  if (utils::ExprMutationAnalyzer(*Function->getBody(), *Result.Context)
+  if (ExprMutationAnalyzer(*Function->getBody(), *Result.Context)
   .isMutated(Param))
 return;
   // CXXCtorInitializer might also mutate Param but they're not part of function
   // body, so check them separately here.
   if (const auto *Ctor = dyn_cast(Function)) {
 for (const auto *Init : Ctor->inits()) {
-  if (utils::ExprMutationAnalyzer(*Init->getInit(), *Result.Context)
+  if (ExprMutationAnalyzer(*Init->getInit(), *Result.Context)
   .isMutated(Param))
 return;
 }
Index: clang-tidy/utils/CMakeLists.txt
===
--- clang-tidy/utils/CMakeLists.txt
+++ clang-tidy/utils/CMakeLists.txt
@@ -3,7 +3,6 @@
 add_clang_library(clangTidyUtils
   ASTUtils.cpp
   DeclRefExprUtils.cpp
-  ExprMutationAnalyzer.cpp
   ExprSequence.cpp
   FixItHintUtils.cpp
   HeaderFileExtensionsUtils.cpp
Index: unittests/clang-tidy/CMakeLists.txt
===
--- unittests/clang-tidy/CMakeLists.txt
+++ unittests/clang-tidy/CMakeLists.txt
@@ -9,7 +9,6 @@
 add_extra_unittest(ClangTidyTests
   ClangTidyDiagnosticConsumerTest.cpp
   ClangTidyOptionsTest.cpp
-  ExprMutationAnalyzerTest.cpp
   IncludeInserterTest.cpp
   GoogleModuleTest.cpp
   LLVMModuleTest.cpp
Index: clang-tidy/utils/ExprMutationAnalyzer.h
===
--- clang-tidy/utils/ExprMutationAnalyzer.h
+++ clang-tidy/utils/ExprMutationAnalyzer.h
@@ -1,56 +0,0 @@
-//===-- ExprMutationAnalyzer.h - clang-tidy ---===//
-//
-// 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_EXPRMUTATIONANALYZER_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_EXPRMUTATIONANALYZER_H
-
-#include 
-
-#include "clang/AST/AST.h"
-#include "clang/ASTMatchers/ASTMatchers.h"
-#include "llvm/ADT/DenseMap.h"
-
-namespace clang {
-namespace tidy {
-namespace utils {
-
-/// Analyzes whether any mutative operations are applied to an expression within
-/// a given statement.
-class ExprMutationA

[PATCH] D52008: [analyzer] Handle forwarding reference better in ExprMutationAnalyzer.

2018-09-12 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
shuaiwang added reviewers: lebedev.ri, JonasToth.
Herald added subscribers: cfe-commits, Szelethus, mikhail.ramalho, a.sidorin, 
szepet, xazax.hun.
Herald added a reviewer: george.karpenkov.

We used to treat an `Expr` mutated whenever it's passed as non-const
reference argument to a function. This results in false positives in
cases like this:

  int x;
  std::vector v;
  v.emplace_back(x); // `x` is passed as non-const reference to `emplace_back`

In theory the false positives can be suppressed with
`v.emplace_back(std::as_const(x))` but that's considered overly verbose,
inconsistent with existing code and spammy as diags.

This diff handles such cases by following into the function definition
and see whether the argument is mutated inside.


Repository:
  rC Clang

https://reviews.llvm.org/D52008

Files:
  include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
  lib/Analysis/ExprMutationAnalyzer.cpp
  unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: unittests/Analysis/ExprMutationAnalyzerTest.cpp
===
--- unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -595,6 +595,68 @@
   EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
+TEST(ExprMutationAnalyzerTest, FollowFuncArgModified) {
+  auto AST =
+  tooling::buildASTFromCode("template  void g(T&& t) { t = 10; }"
+"void f() { int x; g(x); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "void h(int&);"
+  "template  void g(Args&&... args) { h(args...); }"
+  "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "void h(int&, int);"
+  "template  void g(Args&&... args) { h(args...); }"
+  "void f() { int x, y; g(x, y); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x, y)"));
+
+  AST = tooling::buildASTFromCode(
+  "void h(int, int&);"
+  "template  void g(Args&&... args) { h(args...); }"
+  "void f() { int x, y; g(y, x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(y, x)"));
+}
+
+TEST(ExprMutationAnalyzerTest, FollowFuncArgNotModified) {
+  auto AST = tooling::buildASTFromCode("template  void g(T&&) {}"
+   "void f() { int x; g(x); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode("template  void g(T&& t) { t; }"
+  "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST =
+  tooling::buildASTFromCode("template  void g(Args&&...) {}"
+"void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST =
+  tooling::buildASTFromCode("template  void g(Args&&...) {}"
+"void f() { int y, x; g(y, x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "void h(int, int&);"
+  "template  void g(Args&&... args) { h(args...); }"
+  "void f() { int x, y; g(x, y); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
 TEST(ExprMutationAnalyzerTest, ArrayElementModified) {
   const auto AST =
   tooling::buildASTFromCode("void f() { int x[2]; x[0] = 10; }");
Index: lib/Analysis/ExprMutationAnalyzer.cpp
===
--- lib/Analysis/ExprMutationAnalyzer.cpp
+++ lib/Analysis/ExprMutationAnalyzer.cpp
@@ -79,7 +79,8 @@
  &ExprMutationAnalyzer::findArrayElementMutation,
  &ExprMutationAnalyzer::findCastMutation,
  &ExprMutationAnalyzer::findRangeLoopMutation,
- &ExprMutationAnalyzer::findReferenceMutation}) {
+ &ExprMutationAnalyzer::findReferenceMutation,
+ &ExprMutationAnalyzer::findFunctionArgMutation}) {
 if (const Stmt *S = (this->*Finder)(Exp))
   return Results[Exp] = S;
   }
@@ -192,10 +193,15 @@
 
   //

[PATCH] D52008: [analyzer] Handle forwarding reference better in ExprMutationAnalyzer.

2018-09-13 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

Just some quick comments, I'll take a deeper look into other comments later.

This diff along unfortunately won't be able to handle `emplace_back` just yet, 
the reason (I believe, haven't fully tested) is that `std::forward` is not 
handled properly and almost all std functions involving forwarding references 
at least `std::forward`'ed once.
I have some more changes locally that treats `std::move` and `std::forward` 
just as casts and that should be able to really push the analysis further down 
the forwarding chain instead of stopping at `std::forward` call.
Rephased diff description to be more clear. Sorry for the confusion.




Comment at: include/clang/Analysis/Analyses/ExprMutationAnalyzer.h:60
+public:
+  FunctionParmMutationAnalyzer(const FunctionDecl &Func, ASTContext &Context);
+

JonasToth wrote:
> Why do we need a separate class for this?
> I think you can just create another object of `ExprMutAnalyzer` and do the 
> analysis with `findDeclMutation(FunctioParm)`.
> 
> The `Stmt` is the `functionDecl().getBody()`. Right now it could be that you 
> pass in an functionDecl without body.
> 
> Could there something happen with extern templates that the body is not 
> accessible?
It's mostly for the special handling of constructors in which case initializer 
list also could mutate param.


Repository:
  rC Clang

https://reviews.llvm.org/D52008



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D52008: [analyzer] Handle forwarding reference better in ExprMutationAnalyzer.

2018-09-13 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 165420.
shuaiwang marked 10 inline comments as done.
shuaiwang added a comment.

More test cases addressing review comments


Repository:
  rC Clang

https://reviews.llvm.org/D52008

Files:
  include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
  lib/Analysis/ExprMutationAnalyzer.cpp
  unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: unittests/Analysis/ExprMutationAnalyzerTest.cpp
===
--- unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -595,6 +595,96 @@
   EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
+TEST(ExprMutationAnalyzerTest, FollowFuncArgModified) {
+  auto AST =
+  tooling::buildASTFromCode("template  void g(T&& t) { t = 10; }"
+"void f() { int x; g(x); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "void h(int&);"
+  "template  void g(Args&&... args) { h(args...); }"
+  "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "void h(int&, int);"
+  "template  void g(Args&&... args) { h(args...); }"
+  "void f() { int x, y; g(x, y); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x, y)"));
+  Results = match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "void h(int, int&);"
+  "template  void g(Args&&... args) { h(args...); }"
+  "void f() { int x, y; g(y, x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(y, x)"));
+  Results = match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "struct S { template  S(T&& t) { t = 10; } };"
+  "void f() { int x; S s(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
+
+  AST = tooling::buildASTFromCode(
+  "struct S { template  S(T&& t) : m(++t) { } int m; };"
+  "void f() { int x; S s(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
+}
+
+TEST(ExprMutationAnalyzerTest, FollowFuncArgNotModified) {
+  auto AST = tooling::buildASTFromCode("template  void g(T&&) {}"
+   "void f() { int x; g(x); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode("template  void g(T&& t) { t; }"
+  "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST =
+  tooling::buildASTFromCode("template  void g(Args&&...) {}"
+"void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST =
+  tooling::buildASTFromCode("template  void g(Args&&...) {}"
+"void f() { int y, x; g(y, x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "void h(int, int&);"
+  "template  void g(Args&&... args) { h(args...); }"
+  "void f() { int x, y; g(x, y); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "struct S { template  S(T&& t) { t; } };"
+  "void f() { int x; S s(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "struct S { template  S(T&& t) : m(t) { } int m; };"
+  "void f() { int x; S s(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
 TEST(ExprMutationAnalyzerTest, ArrayElementModified) {
   const auto AST =
   tooling::buildASTFromCode("void f() { int x[2]; x[0] = 10; }");
Index: lib/Analysis/ExprMutationAnalyzer.cpp
==

[PATCH] D52008: [analyzer] Handle forwarding reference better in ExprMutationAnalyzer.

2018-09-13 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D52008#1232923, @lebedev.ri wrote:

> Thanks for working on this! I tried, and it appears to not fix the issue at 
> hand.
>
> - ``` struct C1 { C1(const C1* c, int num); };
>
>   int x = 0; auto y = std::make_unique(nullptr, x); // <- still 
> considered a mutation? ``` * ``` struct C3 {}; // some class
>
>   struct C2 { C2(const int* whatever, int n, C3 zz); };
>
>   int x = 0; std::vector v; v.emplace_back(nullptr, x, {}); // <- still 
> considered a mutation? ```
>
>   And so on. These are hand-reduced, so hopefully you can reproduce?


I've patched https://reviews.llvm.org/D51870 and tried these cases, they work 
correctly with this change plus the change making `std::move` & `std::forward` 
considered casts. I also tested

  struct D {
D(int&);
  };
  void h() {
std::vector v;
for (int i = 0; i < 10; ++i) {
  v.emplace_back(i);
}
  }

and that's also correctly considered as mutation at `emplace_back`




Comment at: include/clang/Analysis/Analyses/ExprMutationAnalyzer.h:60
+public:
+  FunctionParmMutationAnalyzer(const FunctionDecl &Func, ASTContext &Context);
+

JonasToth wrote:
> shuaiwang wrote:
> > JonasToth wrote:
> > > Why do we need a separate class for this?
> > > I think you can just create another object of `ExprMutAnalyzer` and do 
> > > the analysis with `findDeclMutation(FunctioParm)`.
> > > 
> > > The `Stmt` is the `functionDecl().getBody()`. Right now it could be that 
> > > you pass in an functionDecl without body.
> > > 
> > > Could there something happen with extern templates that the body is not 
> > > accessible?
> > It's mostly for the special handling of constructors in which case 
> > initializer list also could mutate param.
> Hmm, still why not within `ExprMutAnalyzer`?
> You could make it a class living within ExprMutAnalyzer in the private 
> section. Then its clear its an implementation detail.
Oh actually I'm planning to use this also in UnnecessaryValueParamCheck



Comment at: lib/Analysis/ExprMutationAnalyzer.cpp:201
   equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType(;
+  const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
   const auto AsNonConstRefArg = anyOf(

JonasToth wrote:
> I think this will not all cases correctly.
> 
> Say
> ```
> template 
> struct Foo {
>   static void bar() { SomethingWith(T()); }
> };
> ```
> 
> `bar` it self is not a template and `NotInstantiated` will be `false` (only 
> 90% sure on that) as the declaration of `bar` does not match.
> In another check I had to do this: `unless(has(expr(anyOf(isTypeDependent(), 
> isValueDependent()` to ensure that there are no unresolved constructs in 
> the stmt.
I think it's fine here since we only care about skipping those with forwarding 
references.



Comment at: lib/Analysis/ExprMutationAnalyzer.cpp:367
+// function body, check them eagerly here since they're typically trivial.
+for (const CXXCtorInitializer *Init : Ctor->inits()) {
+  ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context);

JonasToth wrote:
> Just to be sure, this will recurse ?
> 
> ```
> struct Foo {
>   std::string s;
>   Foo(std::string s) : s (std::move(s)) {}
> };
> ```
> 
> `std::move` will be resolved through the new mechanism right?
This diff along won't handle `std::move` "properly" yet.
We'll look into definition of `std::move`, it'll be something like
```
return static_cast::type&&>(t);
```
and we'll simply conclude `t` is mutated: because it's being returned.

So for you case, we'll see `s` is mutated by `std::move(s)` and stop there, 
when we treat `std::move` as cast, we'll go one step further and found 
`std::move(s)` is passed as non-const argument to constructor of std::string, 
and we'll stop there concluding `s` is mutated.


Repository:
  rC Clang

https://reviews.llvm.org/D52008



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D51946: [analyzer] Remove PseudoConstantAnalysis

2018-09-14 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL342246: Remove PseudoConstantAnalysis (authored by 
shuaiwang, committed by ).
Herald added a subscriber: llvm-commits.

Repository:
  rL LLVM

https://reviews.llvm.org/D51946

Files:
  cfe/trunk/include/clang/Analysis/Analyses/PseudoConstantAnalysis.h
  cfe/trunk/include/clang/Analysis/AnalysisDeclContext.h
  cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp
  cfe/trunk/lib/Analysis/CMakeLists.txt
  cfe/trunk/lib/Analysis/PseudoConstantAnalysis.cpp

Index: cfe/trunk/lib/Analysis/CMakeLists.txt
===
--- cfe/trunk/lib/Analysis/CMakeLists.txt
+++ cfe/trunk/lib/Analysis/CMakeLists.txt
@@ -23,7 +23,6 @@
   PostOrderCFGView.cpp
   PrintfFormatString.cpp
   ProgramPoint.cpp
-  PseudoConstantAnalysis.cpp
   ReachableCode.cpp
   ScanfFormatString.cpp
   ThreadSafety.cpp
Index: cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp
===
--- cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp
+++ cfe/trunk/lib/Analysis/AnalysisDeclContext.cpp
@@ -27,7 +27,6 @@
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
-#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h"
 #include "clang/Analysis/BodyFarm.h"
 #include "clang/Analysis/CFG.h"
 #include "clang/Analysis/CFGStmtMap.h"
@@ -292,12 +291,6 @@
   return *PM;
 }
 
-PseudoConstantAnalysis *AnalysisDeclContext::getPseudoConstantAnalysis() {
-  if (!PCA)
-PCA.reset(new PseudoConstantAnalysis(getBody()));
-  return PCA.get();
-}
-
 AnalysisDeclContext *AnalysisDeclContextManager::getContext(const Decl *D) {
   if (const auto *FD = dyn_cast(D)) {
 // Calling 'hasBody' replaces 'FD' in place with the FunctionDecl
Index: cfe/trunk/include/clang/Analysis/AnalysisDeclContext.h
===
--- cfe/trunk/include/clang/Analysis/AnalysisDeclContext.h
+++ cfe/trunk/include/clang/Analysis/AnalysisDeclContext.h
@@ -40,7 +40,6 @@
 class LocationContext;
 class LocationContextManager;
 class ParentMap;
-class PseudoConstantAnalysis;
 class StackFrameContext;
 class Stmt;
 class VarDecl;
@@ -84,7 +83,6 @@
   bool builtCFG = false;
   bool builtCompleteCFG = false;
   std::unique_ptr PM;
-  std::unique_ptr PCA;
   std::unique_ptr CFA;
 
   llvm::BumpPtrAllocator A;
@@ -175,7 +173,6 @@
   bool isCFGBuilt() const { return builtCFG; }
 
   ParentMap &getParentMap();
-  PseudoConstantAnalysis *getPseudoConstantAnalysis();
 
   using referenced_decls_iterator = const VarDecl * const *;
 
Index: cfe/trunk/lib/Analysis/PseudoConstantAnalysis.cpp
===
--- cfe/trunk/lib/Analysis/PseudoConstantAnalysis.cpp
+++ cfe/trunk/lib/Analysis/PseudoConstantAnalysis.cpp
@@ -1,226 +0,0 @@
-//== PseudoConstantAnalysis.cpp - Find Pseudoconstants in the AST-*- C++ -*-==//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===--===//
-//
-// This file tracks the usage of variables in a Decl body to see if they are
-// never written to, implying that they constant. This is useful in static
-// analysis to see if a developer might have intended a variable to be const.
-//
-//===--===//
-
-#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/Expr.h"
-#include "clang/AST/Stmt.h"
-#include "llvm/ADT/SmallPtrSet.h"
-#include 
-
-using namespace clang;
-
-typedef llvm::SmallPtrSet VarDeclSet;
-
-PseudoConstantAnalysis::PseudoConstantAnalysis(const Stmt *DeclBody) :
-  DeclBody(DeclBody), Analyzed(false) {
-  NonConstantsImpl = new VarDeclSet;
-  UsedVarsImpl = new VarDeclSet;
-}
-
-PseudoConstantAnalysis::~PseudoConstantAnalysis() {
-  delete (VarDeclSet*)NonConstantsImpl;
-  delete (VarDeclSet*)UsedVarsImpl;
-}
-
-// Returns true if the given ValueDecl is never written to in the given DeclBody
-bool PseudoConstantAnalysis::isPseudoConstant(const VarDecl *VD) {
-  // Only local and static variables can be pseudoconstants
-  if (!VD->hasLocalStorage() && !VD->isStaticLocal())
-return false;
-
-  if (!Analyzed) {
-RunAnalysis();
-Analyzed = true;
-  }
-
-  VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl;
-
-  return !NonConstants->count(VD);
-}
-
-// Returns true if the variable was used (self assignments don't count)
-bool PseudoConstantAnalysis::wasReferenced(const VarDecl *VD) {
-  if (!Analyzed) {
-RunAnalysis();
-Analyzed = true;
-  }
-
-  VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl;
-
-  return UsedVars->count(VD);
-}
-
-// Returns a Decl from a (

[PATCH] D52008: [analyzer] Handle forwarding reference better in ExprMutationAnalyzer.

2018-09-14 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 165552.
shuaiwang marked 8 inline comments as done.
shuaiwang added a comment.

Addressed review comments.


Repository:
  rC Clang

https://reviews.llvm.org/D52008

Files:
  include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
  lib/Analysis/ExprMutationAnalyzer.cpp
  unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: unittests/Analysis/ExprMutationAnalyzerTest.cpp
===
--- unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -595,6 +595,96 @@
   EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
+TEST(ExprMutationAnalyzerTest, FollowFuncArgModified) {
+  auto AST =
+  tooling::buildASTFromCode("template  void g(T&& t) { t = 10; }"
+"void f() { int x; g(x); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "void h(int&);"
+  "template  void g(Args&&... args) { h(args...); }"
+  "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
+
+  AST = tooling::buildASTFromCode(
+  "void h(int&, int);"
+  "template  void g(Args&&... args) { h(args...); }"
+  "void f() { int x, y; g(x, y); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x, y)"));
+  Results = match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "void h(int, int&);"
+  "template  void g(Args&&... args) { h(args...); }"
+  "void f() { int x, y; g(y, x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(y, x)"));
+  Results = match(withEnclosingCompound(declRefTo("y")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "struct S { template  S(T&& t) { t = 10; } };"
+  "void f() { int x; S s(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
+
+  AST = tooling::buildASTFromCode(
+  "struct S { template  S(T&& t) : m(++t) { } int m; };"
+  "void f() { int x; S s(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
+}
+
+TEST(ExprMutationAnalyzerTest, FollowFuncArgNotModified) {
+  auto AST = tooling::buildASTFromCode("template  void g(T&&) {}"
+   "void f() { int x; g(x); }");
+  auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode("template  void g(T&& t) { t; }"
+  "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST =
+  tooling::buildASTFromCode("template  void g(Args&&...) {}"
+"void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST =
+  tooling::buildASTFromCode("template  void g(Args&&...) {}"
+"void f() { int y, x; g(y, x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "void h(int, int&);"
+  "template  void g(Args&&... args) { h(args...); }"
+  "void f() { int x, y; g(x, y); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "struct S { template  S(T&& t) { t; } };"
+  "void f() { int x; S s(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "struct S { template  S(T&& t) : m(t) { } int m; };"
+  "void f() { int x; S s(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
 TEST(ExprMutationAnalyzerTest, ArrayElementModified) {
   const auto AST =
   tooling::buildASTFromCode("void f() { int x[2]; x[0] = 10; }");
Index: lib/Analysis/ExprMutationAnalyzer.cpp
===

[PATCH] D52008: [analyzer] Handle forwarding reference better in ExprMutationAnalyzer.

2018-09-14 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D52008#1234828, @JonasToth wrote:

> The `std::move` as cast is a follow up patch?


Yes I'll send a follow up patch.




Comment at: lib/Analysis/ExprMutationAnalyzer.cpp:381
+FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
+  const auto Memoized = Results.find(Parm);
+  if (Memoized != Results.end())

JonasToth wrote:
> Please spell out the type here
This type is a bit cumbersome to spell out as it's an iterator. I feel it's 
fine to keep it auto.


Repository:
  rC Clang

https://reviews.llvm.org/D52008



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D52008: [analyzer] Handle forwarding reference better in ExprMutationAnalyzer.

2018-09-14 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL342271: [analyzer] Handle forwarding reference better in 
ExprMutationAnalyzer. (authored by shuaiwang, committed by ).
Herald added a subscriber: llvm-commits.

Repository:
  rL LLVM

https://reviews.llvm.org/D52008

Files:
  cfe/trunk/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
  cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp
  cfe/trunk/unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp
===
--- cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp
+++ cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -79,7 +79,8 @@
  &ExprMutationAnalyzer::findArrayElementMutation,
  &ExprMutationAnalyzer::findCastMutation,
  &ExprMutationAnalyzer::findRangeLoopMutation,
- &ExprMutationAnalyzer::findReferenceMutation}) {
+ &ExprMutationAnalyzer::findReferenceMutation,
+ &ExprMutationAnalyzer::findFunctionArgMutation}) {
 if (const Stmt *S = (this->*Finder)(Exp))
   return Results[Exp] = S;
   }
@@ -192,10 +193,15 @@
 
   // Used as non-const-ref argument when calling a function.
   // An argument is assumed to be non-const-ref when the function is unresolved.
+  // Instantiated template functions are not handled here but in
+  // findFunctionArgMutation which has additional smarts for handling forwarding
+  // references.
   const auto NonConstRefParam = forEachArgumentWithParam(
   equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType(;
+  const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
   const auto AsNonConstRefArg = anyOf(
-  callExpr(NonConstRefParam), cxxConstructExpr(NonConstRefParam),
+  callExpr(NonConstRefParam, NotInstantiated),
+  cxxConstructExpr(NonConstRefParam, NotInstantiated),
   callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
  cxxDependentScopeMemberExpr(),
  hasType(templateTypeParmType(),
@@ -305,4 +311,82 @@
   return findDeclMutation(Refs);
 }
 
+const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) {
+  const auto NonConstRefParam = forEachArgumentWithParam(
+  equalsNode(Exp),
+  parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
+  const auto IsInstantiated = hasDeclaration(isInstantiated());
+  const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
+  const auto Matches = match(
+  findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl),
+ cxxConstructExpr(NonConstRefParam, IsInstantiated,
+  FuncDecl)))
+  .bind("expr")),
+  Stm, Context);
+  for (const auto &Nodes : Matches) {
+const auto *Exp = Nodes.getNodeAs("expr");
+const auto *Func = Nodes.getNodeAs("func");
+if (!Func->getBody())
+  return Exp;
+
+const auto *Parm = Nodes.getNodeAs("parm");
+const ArrayRef AllParams =
+Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
+QualType ParmType =
+AllParams[std::min(Parm->getFunctionScopeIndex(),
+   AllParams.size() - 1)]
+->getType();
+if (const auto *T = ParmType->getAs())
+  ParmType = T->getPattern();
+
+// If param type is forwarding reference, follow into the function
+// definition and see whether the param is mutated inside.
+if (const auto *RefType = ParmType->getAs()) {
+  if (!RefType->getPointeeType().getQualifiers() &&
+  RefType->getPointeeType()->getAs()) {
+std::unique_ptr &Analyzer =
+FuncParmAnalyzer[Func];
+if (!Analyzer)
+  Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context));
+if (Analyzer->findMutation(Parm))
+  return Exp;
+continue;
+  }
+}
+// Not forwarding reference.
+return Exp;
+  }
+  return nullptr;
+}
+
+FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
+const FunctionDecl &Func, ASTContext &Context)
+: BodyAnalyzer(*Func.getBody(), Context) {
+  if (const auto *Ctor = dyn_cast(&Func)) {
+// CXXCtorInitializer might also mutate Param but they're not part of
+// function body, check them eagerly here since they're typically trivial.
+for (const CXXCtorInitializer *Init : Ctor->inits()) {
+  ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context);
+  for (const ParmVarDecl *Parm : Ctor->parameters()) {
+if (Results.find(Parm) != Results.end())
+  continue;
+if (const Stmt *S = InitAnalyzer.findDeclMutation(Parm))
+  Results[Parm] = S;
+  }
+}
+  }
+}
+
+const Stmt *
+FunctionParmMut

[PATCH] D52120: [analyzer] Treat std::{move, forward} as casts in ExprMutationAnalyzer.

2018-09-14 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
shuaiwang added reviewers: lebedev.ri, JonasToth.
Herald added subscribers: cfe-commits, Szelethus, mikhail.ramalho, a.sidorin, 
szepet, xazax.hun.
Herald added a reviewer: george.karpenkov.

This is a follow up of https://reviews.llvm.org/D52008 and should make the 
analyzer being able to handle perfect forwardings in real world cases where 
forwardings are done through multiple layers of function calls with 
`std::forward`.

Fixes PR38891.


Repository:
  rC Clang

https://reviews.llvm.org/D52120

Files:
  lib/Analysis/ExprMutationAnalyzer.cpp
  unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: unittests/Analysis/ExprMutationAnalyzerTest.cpp
===
--- unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -373,36 +373,46 @@
 }
 
 TEST(ExprMutationAnalyzerTest, Move) {
-  // Technically almost the same as ByNonConstRRefArgument, just double checking
-  const auto AST = tooling::buildASTFromCode(
+  const std::string Move =
   "namespace std {"
   "template struct remove_reference { typedef T type; };"
   "template struct remove_reference { typedef T type; };"
   "template struct remove_reference { typedef T type; };"
   "template typename std::remove_reference::type&& "
-  "move(T&& t) noexcept; }"
-  "void f() { struct A {}; A x; std::move(x); }");
-  const auto Results =
+  "move(T&& t) noexcept {} }";
+  auto AST = tooling::buildASTFromCode(
+  Move + "void f() { struct A {}; A x; std::move(x); }");
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x)"));
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  Move + "void f() { struct A {}; A x, y; std::move(x) = y; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x) = y"));
 }
 
 TEST(ExprMutationAnalyzerTest, Forward) {
-  // Technically almost the same as ByNonConstRefArgument, just double checking
-  const auto AST = tooling::buildASTFromCode(
+  const std::string Forward =
   "namespace std {"
   "template struct remove_reference { typedef T type; };"
   "template struct remove_reference { typedef T type; };"
   "template struct remove_reference { typedef T type; };"
   "template T&& "
-  "forward(typename std::remove_reference::type&) noexcept;"
+  "forward(typename std::remove_reference::type&) noexcept {}"
   "template T&& "
-  "forward(typename std::remove_reference::type&&) noexcept;"
-  "void f() { struct A {}; A x; std::forward(x); }");
-  const auto Results =
+  "forward(typename std::remove_reference::type&&) noexcept {} }";
+  auto AST = tooling::buildASTFromCode(
+  Forward + "void f() { struct A {}; A x; std::forward(x); }");
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  Forward + "void f() { struct A {}; A x, y; std::forward(x) = y; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()),
-  ElementsAre("std::forward(x)"));
+  ElementsAre("std::forward(x) = y"));
 }
 
 TEST(ExprMutationAnalyzerTest, CallUnresolved) {
@@ -639,6 +649,26 @@
   "void f() { int x; S s(x); }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x"));
+
+  AST = tooling::buildASTFromCode(
+  "namespace std {"
+  "template struct remove_reference { typedef T type; };"
+  "template struct remove_reference { typedef T type; };"
+  "template struct remove_reference { typedef T type; };"
+  "template T&& "
+  "forward(typename std::remove_reference::type& t) noexcept"
+  "{ return t; }"
+  "template T&& "
+  "forward(typename std::remove_reference::type&& t) noexcept"
+  "{ return t; } }"
+  "template  void u(Args&...);"
+  "template  void h(Args&&... args)"
+  "{ u(std::forward(args)...); }"
+  "template  void g(Args&&... args)"
+  "{ h(std::forward(args)...); }"
+  "void f() { int x; g(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)"));
 }
 
 TEST(ExprMutationAnalyzerTest, FollowFuncArgNotModified) {
@@ -683,6 +713,26 @@
   "void f() { int x; S s(x); }");
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  "namespace std {"
+  "template struct remove_reference {

[PATCH] D52120: [analyzer] Treat std::{move, forward} as casts in ExprMutationAnalyzer.

2018-09-14 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

@lebedev.ri could you help test whether this fully resolves PR38891? Thanks!


Repository:
  rC Clang

https://reviews.llvm.org/D52120



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45679: [clang-tidy] Add ExprMutationAnalyzer, that analyzes whether an expression is mutated within a statement.

2018-07-31 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D45679#1183116, @george.karpenkov wrote:

> @aaron.ballman @alexfh @shuaiwang Would it be possible to move that code into 
> a matcher, or into a something which could be used from Clang? We would also 
> like to use similar functionality, but can't include stuff from clang-tidy.


I think it should in theory be possible, though I need some advice about where 
exactly is the best place in clang.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45679



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50102: Use ExprMutationAnalyzer in performance-unnecessary-value-param

2018-07-31 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
Herald added a reviewer: george.karpenkov.
Herald added subscribers: cfe-commits, a.sidorin.

This yields better recall as ExprMutationAnalyzer is more accurate.

One common pattern this check is now able to catch is:

  void foo(std::vector v) {
for (const auto& elm : v) {
  // ...
}
  }


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50102

Files:
  clang-tidy/performance/UnnecessaryValueParamCheck.cpp


Index: clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===
--- clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -10,6 +10,7 @@
 #include "UnnecessaryValueParamCheck.h"
 
 #include "../utils/DeclRefExprUtils.h"
+#include "../utils/ExprMutationAnalyzer.h"
 #include "../utils/FixItHintUtils.h"
 #include "../utils/Matchers.h"
 #include "../utils/TypeTraits.h"
@@ -98,43 +99,52 @@
 void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) 
{
   const auto *Param = Result.Nodes.getNodeAs("param");
   const auto *Function = Result.Nodes.getNodeAs("functionDecl");
-  const size_t Index = std::find(Function->parameters().begin(),
- Function->parameters().end(), Param) -
-   Function->parameters().begin();
-  bool IsConstQualified =
-  Param->getType().getCanonicalType().isConstQualified();
-
-  auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
-  *Param, *Function, *Result.Context);
-  auto ConstDeclRefExprs = utils::decl_ref_expr::constReferenceDeclRefExprs(
-  *Param, *Function, *Result.Context);
 
   // Do not trigger on non-const value parameters when they are not only used 
as
   // const.
-  if (!isSubset(AllDeclRefExprs, ConstDeclRefExprs))
+  if (utils::ExprMutationAnalyzer(Function->getBody(), Result.Context)
+  .isMutated(Param))
 return;
+  if (const auto *Ctor = dyn_cast(Function)) {
+for (const auto *Init : Ctor->inits()) {
+  if (utils::ExprMutationAnalyzer(Init->getInit(), Result.Context)
+  .isMutated(Param))
+return;
+}
+  }
+
+  const bool IsConstQualified =
+  Param->getType().getCanonicalType().isConstQualified();
 
   // If the parameter is non-const, check if it has a move constructor and is
   // only referenced once to copy-construct another object or whether it has a
   // move assignment operator and is only referenced once when copy-assigned.
   // In this case wrap DeclRefExpr with std::move() to avoid the unnecessary
   // copy.
-  if (!IsConstQualified && AllDeclRefExprs.size() == 1) {
-auto CanonicalType = Param->getType().getCanonicalType();
-const auto &DeclRefExpr  = **AllDeclRefExprs.begin();
-
-if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) &&
-((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) &&
-  utils::decl_ref_expr::isCopyConstructorArgument(
-  DeclRefExpr, *Function, *Result.Context)) ||
- (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) &&
-  utils::decl_ref_expr::isCopyAssignmentArgument(
-  DeclRefExpr, *Function, *Result.Context {
-  handleMoveFix(*Param, DeclRefExpr, *Result.Context);
-  return;
+  if (!IsConstQualified) {
+auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
+*Param, *Function, *Result.Context);
+if (AllDeclRefExprs.size() == 1) {
+  auto CanonicalType = Param->getType().getCanonicalType();
+  const auto &DeclRefExpr = **AllDeclRefExprs.begin();
+
+  if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) &&
+  ((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) &&
+utils::decl_ref_expr::isCopyConstructorArgument(
+DeclRefExpr, *Function, *Result.Context)) ||
+   (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) &&
+utils::decl_ref_expr::isCopyAssignmentArgument(
+DeclRefExpr, *Function, *Result.Context {
+handleMoveFix(*Param, DeclRefExpr, *Result.Context);
+return;
+  }
 }
   }
 
+  const size_t Index = std::find(Function->parameters().begin(),
+ Function->parameters().end(), Param) -
+   Function->parameters().begin();
+
   auto Diag =
   diag(Param->getLocation(),
IsConstQualified ? "the const qualified parameter %0 is "


Index: clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===
--- clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -10,6 +10,7 @@
 #include "UnnecessaryValueParamCheck.h"
 
 #include "../utils/DeclRefExprUtils.h"
+#include "../utils/ExprMutationAnalyzer.h"
 #include "../utils/FixItHintUtils.h"

[PATCH] D50102: [clang-tidy] Use ExprMutationAnalyzer in performance-unnecessary-value-param

2018-07-31 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 158408.
shuaiwang added a comment.

Add test case


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50102

Files:
  clang-tidy/performance/UnnecessaryValueParamCheck.cpp
  test/clang-tidy/performance-unnecessary-value-param.cpp

Index: test/clang-tidy/performance-unnecessary-value-param.cpp
===
--- test/clang-tidy/performance-unnecessary-value-param.cpp
+++ test/clang-tidy/performance-unnecessary-value-param.cpp
@@ -15,6 +15,20 @@
 void useAsConstReference(const ExpensiveToCopyType &);
 void useByValue(ExpensiveToCopyType);
 
+template  class Vector {
+ public:
+  using iterator = T*;
+  using const_iterator = const T*;
+
+  Vector(const Vector&);
+  Vector& operator=(const Vector&);
+
+  iterator begin();
+  iterator end();
+  const_iterator begin() const;
+  const_iterator end() const;
+};
+
 // This class simulates std::pair<>. It is trivially copy constructible
 // and trivially destructible, but not trivially copy assignable.
 class SomewhatTrivial {
@@ -59,6 +73,14 @@
   useByValue(Obj);
 }
 
+void positiveVector(Vector V) {
+  // CHECK-MESSAGES: [[@LINE-1]]:49: warning: the parameter 'V' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
+  // CHECK-FIXES: void positiveVector(const Vector& V) {
+  for (const auto& Obj : V) {
+useByValue(Obj);
+  }
+}
+
 void positiveWithComment(const ExpensiveToCopyType /* important */ S);
 // CHECK-FIXES: void positiveWithComment(const ExpensiveToCopyType& /* important */ S);
 void positiveWithComment(const ExpensiveToCopyType /* important */ S) {
Index: clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===
--- clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -10,6 +10,7 @@
 #include "UnnecessaryValueParamCheck.h"
 
 #include "../utils/DeclRefExprUtils.h"
+#include "../utils/ExprMutationAnalyzer.h"
 #include "../utils/FixItHintUtils.h"
 #include "../utils/Matchers.h"
 #include "../utils/TypeTraits.h"
@@ -98,43 +99,52 @@
 void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
   const auto *Param = Result.Nodes.getNodeAs("param");
   const auto *Function = Result.Nodes.getNodeAs("functionDecl");
-  const size_t Index = std::find(Function->parameters().begin(),
- Function->parameters().end(), Param) -
-   Function->parameters().begin();
-  bool IsConstQualified =
-  Param->getType().getCanonicalType().isConstQualified();
-
-  auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
-  *Param, *Function, *Result.Context);
-  auto ConstDeclRefExprs = utils::decl_ref_expr::constReferenceDeclRefExprs(
-  *Param, *Function, *Result.Context);
 
   // Do not trigger on non-const value parameters when they are not only used as
   // const.
-  if (!isSubset(AllDeclRefExprs, ConstDeclRefExprs))
+  if (utils::ExprMutationAnalyzer(Function->getBody(), Result.Context)
+  .isMutated(Param))
 return;
+  if (const auto *Ctor = dyn_cast(Function)) {
+for (const auto *Init : Ctor->inits()) {
+  if (utils::ExprMutationAnalyzer(Init->getInit(), Result.Context)
+  .isMutated(Param))
+return;
+}
+  }
+
+  const bool IsConstQualified =
+  Param->getType().getCanonicalType().isConstQualified();
 
   // If the parameter is non-const, check if it has a move constructor and is
   // only referenced once to copy-construct another object or whether it has a
   // move assignment operator and is only referenced once when copy-assigned.
   // In this case wrap DeclRefExpr with std::move() to avoid the unnecessary
   // copy.
-  if (!IsConstQualified && AllDeclRefExprs.size() == 1) {
-auto CanonicalType = Param->getType().getCanonicalType();
-const auto &DeclRefExpr  = **AllDeclRefExprs.begin();
-
-if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) &&
-((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) &&
-  utils::decl_ref_expr::isCopyConstructorArgument(
-  DeclRefExpr, *Function, *Result.Context)) ||
- (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) &&
-  utils::decl_ref_expr::isCopyAssignmentArgument(
-  DeclRefExpr, *Function, *Result.Context {
-  handleMoveFix(*Param, DeclRefExpr, *Result.Context);
-  return;
+  if (!IsConstQualified) {
+auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
+*Param, *Function, *Result.Context);
+if (AllDeclRefExprs.size() == 1) {
+  auto CanonicalType = Param->getType().getCanonicalType();
+  const auto &DeclRefExpr = **AllDeclRefExprs.begin();
+
+  if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) &&
+

[PATCH] D50102: [clang-tidy] Use ExprMutationAnalyzer in performance-unnecessary-value-param

2018-08-02 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 158856.
shuaiwang marked 2 inline comments as done.
shuaiwang added a comment.

Update comments.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50102

Files:
  clang-tidy/performance/UnnecessaryValueParamCheck.cpp
  test/clang-tidy/performance-unnecessary-value-param.cpp

Index: test/clang-tidy/performance-unnecessary-value-param.cpp
===
--- test/clang-tidy/performance-unnecessary-value-param.cpp
+++ test/clang-tidy/performance-unnecessary-value-param.cpp
@@ -15,6 +15,20 @@
 void useAsConstReference(const ExpensiveToCopyType &);
 void useByValue(ExpensiveToCopyType);
 
+template  class Vector {
+ public:
+  using iterator = T*;
+  using const_iterator = const T*;
+
+  Vector(const Vector&);
+  Vector& operator=(const Vector&);
+
+  iterator begin();
+  iterator end();
+  const_iterator begin() const;
+  const_iterator end() const;
+};
+
 // This class simulates std::pair<>. It is trivially copy constructible
 // and trivially destructible, but not trivially copy assignable.
 class SomewhatTrivial {
@@ -59,6 +73,14 @@
   useByValue(Obj);
 }
 
+void positiveVector(Vector V) {
+  // CHECK-MESSAGES: [[@LINE-1]]:49: warning: the parameter 'V' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
+  // CHECK-FIXES: void positiveVector(const Vector& V) {
+  for (const auto& Obj : V) {
+useByValue(Obj);
+  }
+}
+
 void positiveWithComment(const ExpensiveToCopyType /* important */ S);
 // CHECK-FIXES: void positiveWithComment(const ExpensiveToCopyType& /* important */ S);
 void positiveWithComment(const ExpensiveToCopyType /* important */ S) {
Index: clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===
--- clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -10,6 +10,7 @@
 #include "UnnecessaryValueParamCheck.h"
 
 #include "../utils/DeclRefExprUtils.h"
+#include "../utils/ExprMutationAnalyzer.h"
 #include "../utils/FixItHintUtils.h"
 #include "../utils/Matchers.h"
 #include "../utils/TypeTraits.h"
@@ -31,14 +32,6 @@
   .str();
 }
 
-template 
-bool isSubset(const S &SubsetCandidate, const S &SupersetCandidate) {
-  for (const auto &E : SubsetCandidate)
-if (SupersetCandidate.count(E) == 0)
-  return false;
-  return true;
-}
-
 bool isReferencedOutsideOfCallExpr(const FunctionDecl &Function,
ASTContext &Context) {
   auto Matches = match(declRefExpr(to(functionDecl(equalsNode(&Function))),
@@ -98,43 +91,53 @@
 void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
   const auto *Param = Result.Nodes.getNodeAs("param");
   const auto *Function = Result.Nodes.getNodeAs("functionDecl");
-  const size_t Index = std::find(Function->parameters().begin(),
- Function->parameters().end(), Param) -
-   Function->parameters().begin();
-  bool IsConstQualified =
-  Param->getType().getCanonicalType().isConstQualified();
 
-  auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
-  *Param, *Function, *Result.Context);
-  auto ConstDeclRefExprs = utils::decl_ref_expr::constReferenceDeclRefExprs(
-  *Param, *Function, *Result.Context);
-
-  // Do not trigger on non-const value parameters when they are not only used as
-  // const.
-  if (!isSubset(AllDeclRefExprs, ConstDeclRefExprs))
+  // Do not trigger on non-const value parameters when they are mutated either
+  // within the function body or within init expression(s) when the function is
+  // a ctor.
+  if (utils::ExprMutationAnalyzer(Function->getBody(), Result.Context)
+  .isMutated(Param))
 return;
+  if (const auto *Ctor = dyn_cast(Function)) {
+for (const auto *Init : Ctor->inits()) {
+  if (utils::ExprMutationAnalyzer(Init->getInit(), Result.Context)
+  .isMutated(Param))
+return;
+}
+  }
+
+  const bool IsConstQualified =
+  Param->getType().getCanonicalType().isConstQualified();
 
   // If the parameter is non-const, check if it has a move constructor and is
   // only referenced once to copy-construct another object or whether it has a
   // move assignment operator and is only referenced once when copy-assigned.
   // In this case wrap DeclRefExpr with std::move() to avoid the unnecessary
   // copy.
-  if (!IsConstQualified && AllDeclRefExprs.size() == 1) {
-auto CanonicalType = Param->getType().getCanonicalType();
-const auto &DeclRefExpr  = **AllDeclRefExprs.begin();
-
-if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) &&
-((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) &&
-  utils::decl_ref_expr::isCopyConstructorArgument(
-  DeclRefExpr, *Function, *Result.

[PATCH] D50102: [clang-tidy] Use ExprMutationAnalyzer in performance-unnecessary-value-param

2018-08-02 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added inline comments.



Comment at: clang-tidy/performance/UnnecessaryValueParamCheck.cpp:108
 return;
+  if (const auto *Ctor = dyn_cast(Function)) {
+for (const auto *Init : Ctor->inits()) {

hokein wrote:
> Is this a new fix or  a special case not being caught by 
> `ExprMutationAnalyzer`?  Do we have a test case covered it?
It's a special case.
ExprMutationAnalyzer is designed to analyze within a "scope" indicated by a 
Stmt. So when trying to analyze a "function", merely looking at function body 
is not sufficient because CXXCtorInitializer is not part of the function body. 
So far we only care about this special case when trying to figure out whether a 
function param is mutated or not, which is exactly what this check is doing. We 
might consider making this part of ExprMutationAnalyzer but IMO that depends on 
whether there are more use cases of this special case in the future because 
we'll need some tricks there to match different types of top level nodes.

This is already captured in existing unit test case, which basically looks like 
this:
```
class Foo {
public:
  Foo(ExpensiveObj x) : x(std::move(x)) {}
private:
  ExpensiveObj x;
};
```


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50102



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D50102: [clang-tidy] Use ExprMutationAnalyzer in performance-unnecessary-value-param

2018-08-03 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 159037.
shuaiwang marked 2 inline comments as done.
shuaiwang added a comment.

Add comments explaining CXXCtorInitializer check


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D50102

Files:
  clang-tidy/performance/UnnecessaryValueParamCheck.cpp
  test/clang-tidy/performance-unnecessary-value-param.cpp

Index: test/clang-tidy/performance-unnecessary-value-param.cpp
===
--- test/clang-tidy/performance-unnecessary-value-param.cpp
+++ test/clang-tidy/performance-unnecessary-value-param.cpp
@@ -15,6 +15,20 @@
 void useAsConstReference(const ExpensiveToCopyType &);
 void useByValue(ExpensiveToCopyType);
 
+template  class Vector {
+ public:
+  using iterator = T*;
+  using const_iterator = const T*;
+
+  Vector(const Vector&);
+  Vector& operator=(const Vector&);
+
+  iterator begin();
+  iterator end();
+  const_iterator begin() const;
+  const_iterator end() const;
+};
+
 // This class simulates std::pair<>. It is trivially copy constructible
 // and trivially destructible, but not trivially copy assignable.
 class SomewhatTrivial {
@@ -59,6 +73,14 @@
   useByValue(Obj);
 }
 
+void positiveVector(Vector V) {
+  // CHECK-MESSAGES: [[@LINE-1]]:49: warning: the parameter 'V' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
+  // CHECK-FIXES: void positiveVector(const Vector& V) {
+  for (const auto& Obj : V) {
+useByValue(Obj);
+  }
+}
+
 void positiveWithComment(const ExpensiveToCopyType /* important */ S);
 // CHECK-FIXES: void positiveWithComment(const ExpensiveToCopyType& /* important */ S);
 void positiveWithComment(const ExpensiveToCopyType /* important */ S) {
Index: clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===
--- clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -10,6 +10,7 @@
 #include "UnnecessaryValueParamCheck.h"
 
 #include "../utils/DeclRefExprUtils.h"
+#include "../utils/ExprMutationAnalyzer.h"
 #include "../utils/FixItHintUtils.h"
 #include "../utils/Matchers.h"
 #include "../utils/TypeTraits.h"
@@ -31,14 +32,6 @@
   .str();
 }
 
-template 
-bool isSubset(const S &SubsetCandidate, const S &SupersetCandidate) {
-  for (const auto &E : SubsetCandidate)
-if (SupersetCandidate.count(E) == 0)
-  return false;
-  return true;
-}
-
 bool isReferencedOutsideOfCallExpr(const FunctionDecl &Function,
ASTContext &Context) {
   auto Matches = match(declRefExpr(to(functionDecl(equalsNode(&Function))),
@@ -98,43 +91,55 @@
 void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
   const auto *Param = Result.Nodes.getNodeAs("param");
   const auto *Function = Result.Nodes.getNodeAs("functionDecl");
-  const size_t Index = std::find(Function->parameters().begin(),
- Function->parameters().end(), Param) -
-   Function->parameters().begin();
-  bool IsConstQualified =
-  Param->getType().getCanonicalType().isConstQualified();
 
-  auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
-  *Param, *Function, *Result.Context);
-  auto ConstDeclRefExprs = utils::decl_ref_expr::constReferenceDeclRefExprs(
-  *Param, *Function, *Result.Context);
-
-  // Do not trigger on non-const value parameters when they are not only used as
-  // const.
-  if (!isSubset(AllDeclRefExprs, ConstDeclRefExprs))
+  // Do not trigger on non-const value parameters when they are mutated either
+  // within the function body or within init expression(s) when the function is
+  // a ctor.
+  if (utils::ExprMutationAnalyzer(Function->getBody(), Result.Context)
+  .isMutated(Param))
 return;
+  // CXXCtorInitializer might also mutate Param but they're not part of function
+  // body, so check them separately here.
+  if (const auto *Ctor = dyn_cast(Function)) {
+for (const auto *Init : Ctor->inits()) {
+  if (utils::ExprMutationAnalyzer(Init->getInit(), Result.Context)
+  .isMutated(Param))
+return;
+}
+  }
+
+  const bool IsConstQualified =
+  Param->getType().getCanonicalType().isConstQualified();
 
   // If the parameter is non-const, check if it has a move constructor and is
   // only referenced once to copy-construct another object or whether it has a
   // move assignment operator and is only referenced once when copy-assigned.
   // In this case wrap DeclRefExpr with std::move() to avoid the unnecessary
   // copy.
-  if (!IsConstQualified && AllDeclRefExprs.size() == 1) {
-auto CanonicalType = Param->getType().getCanonicalType();
-const auto &DeclRefExpr  = **AllDeclRefExprs.begin();
-
-if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) &&
-((utils::type_trait

[PATCH] D50102: [clang-tidy] Use ExprMutationAnalyzer in performance-unnecessary-value-param

2018-08-03 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL338903: Use ExprMutationAnalyzer in 
performance-unnecessary-value-param (authored by shuaiwang, committed by ).
Herald added a subscriber: llvm-commits.

Repository:
  rL LLVM

https://reviews.llvm.org/D50102

Files:
  clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
  
clang-tools-extra/trunk/test/clang-tidy/performance-unnecessary-value-param.cpp

Index: clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===
--- clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -10,6 +10,7 @@
 #include "UnnecessaryValueParamCheck.h"
 
 #include "../utils/DeclRefExprUtils.h"
+#include "../utils/ExprMutationAnalyzer.h"
 #include "../utils/FixItHintUtils.h"
 #include "../utils/Matchers.h"
 #include "../utils/TypeTraits.h"
@@ -31,14 +32,6 @@
   .str();
 }
 
-template 
-bool isSubset(const S &SubsetCandidate, const S &SupersetCandidate) {
-  for (const auto &E : SubsetCandidate)
-if (SupersetCandidate.count(E) == 0)
-  return false;
-  return true;
-}
-
 bool isReferencedOutsideOfCallExpr(const FunctionDecl &Function,
ASTContext &Context) {
   auto Matches = match(declRefExpr(to(functionDecl(equalsNode(&Function))),
@@ -98,43 +91,55 @@
 void UnnecessaryValueParamCheck::check(const MatchFinder::MatchResult &Result) {
   const auto *Param = Result.Nodes.getNodeAs("param");
   const auto *Function = Result.Nodes.getNodeAs("functionDecl");
-  const size_t Index = std::find(Function->parameters().begin(),
- Function->parameters().end(), Param) -
-   Function->parameters().begin();
-  bool IsConstQualified =
-  Param->getType().getCanonicalType().isConstQualified();
 
-  auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
-  *Param, *Function, *Result.Context);
-  auto ConstDeclRefExprs = utils::decl_ref_expr::constReferenceDeclRefExprs(
-  *Param, *Function, *Result.Context);
-
-  // Do not trigger on non-const value parameters when they are not only used as
-  // const.
-  if (!isSubset(AllDeclRefExprs, ConstDeclRefExprs))
+  // Do not trigger on non-const value parameters when they are mutated either
+  // within the function body or within init expression(s) when the function is
+  // a ctor.
+  if (utils::ExprMutationAnalyzer(Function->getBody(), Result.Context)
+  .isMutated(Param))
 return;
+  // CXXCtorInitializer might also mutate Param but they're not part of function
+  // body, so check them separately here.
+  if (const auto *Ctor = dyn_cast(Function)) {
+for (const auto *Init : Ctor->inits()) {
+  if (utils::ExprMutationAnalyzer(Init->getInit(), Result.Context)
+  .isMutated(Param))
+return;
+}
+  }
+
+  const bool IsConstQualified =
+  Param->getType().getCanonicalType().isConstQualified();
 
   // If the parameter is non-const, check if it has a move constructor and is
   // only referenced once to copy-construct another object or whether it has a
   // move assignment operator and is only referenced once when copy-assigned.
   // In this case wrap DeclRefExpr with std::move() to avoid the unnecessary
   // copy.
-  if (!IsConstQualified && AllDeclRefExprs.size() == 1) {
-auto CanonicalType = Param->getType().getCanonicalType();
-const auto &DeclRefExpr  = **AllDeclRefExprs.begin();
-
-if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) &&
-((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) &&
-  utils::decl_ref_expr::isCopyConstructorArgument(
-  DeclRefExpr, *Function, *Result.Context)) ||
- (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) &&
-  utils::decl_ref_expr::isCopyAssignmentArgument(
-  DeclRefExpr, *Function, *Result.Context {
-  handleMoveFix(*Param, DeclRefExpr, *Result.Context);
-  return;
+  if (!IsConstQualified) {
+auto AllDeclRefExprs = utils::decl_ref_expr::allDeclRefExprs(
+*Param, *Function, *Result.Context);
+if (AllDeclRefExprs.size() == 1) {
+  auto CanonicalType = Param->getType().getCanonicalType();
+  const auto &DeclRefExpr = **AllDeclRefExprs.begin();
+
+  if (!hasLoopStmtAncestor(DeclRefExpr, *Function, *Result.Context) &&
+  ((utils::type_traits::hasNonTrivialMoveConstructor(CanonicalType) &&
+utils::decl_ref_expr::isCopyConstructorArgument(
+DeclRefExpr, *Function, *Result.Context)) ||
+   (utils::type_traits::hasNonTrivialMoveAssignment(CanonicalType) &&
+utils::decl_ref_expr::isCopyAssignmentArgument(
+DeclRefExpr, *Function, *Result.Context {
+handleMoveFix(*Par

[PATCH] D45679: [clang-tidy] Add ExprMutationAnalyzer, that analyzes whether an expression is mutated within a statement.

2018-06-03 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 149659.
shuaiwang marked 5 inline comments as done.
shuaiwang added a comment.

Handle sizeof on VLA.
Added test case for typeof()
Added TODO for handling typeid and generic selection.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45679

Files:
  clang-tidy/utils/CMakeLists.txt
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  clang-tidy/utils/ExprMutationAnalyzer.h
  unittests/clang-tidy/CMakeLists.txt
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- /dev/null
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -0,0 +1,591 @@
+//===-- ExprMutationAnalyzerTest.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 "../clang-tidy/utils/ExprMutationAnalyzer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+using namespace clang::ast_matchers;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::ResultOf;
+using ::testing::StartsWith;
+using ::testing::Values;
+
+namespace {
+
+using ExprMatcher = internal::Matcher;
+using StmtMatcher = internal::Matcher;
+
+ExprMatcher declRefTo(StringRef Name) {
+  return declRefExpr(to(namedDecl(hasName(Name;
+}
+
+StmtMatcher withEnclosingCompound(ExprMatcher Matcher) {
+  return expr(Matcher, hasAncestor(compoundStmt().bind("stmt"))).bind("expr");
+}
+
+bool isMutated(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  const auto *const E = selectFirst("expr", Results);
+  return utils::ExprMutationAnalyzer(S, &AST->getASTContext()).isMutated(E);
+}
+
+SmallVector
+mutatedBy(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  SmallVector Chain;
+  utils::ExprMutationAnalyzer Analyzer(S, &AST->getASTContext());
+  for (const auto *E = selectFirst("expr", Results); E != nullptr;) {
+const Stmt *By = Analyzer.findMutation(E);
+std::string buffer;
+llvm::raw_string_ostream stream(buffer);
+By->printPretty(stream, nullptr, AST->getASTContext().getPrintingPolicy());
+Chain.push_back(StringRef(stream.str()).trim().str());
+E = dyn_cast(By);
+  }
+  return Chain;
+}
+
+std::string removeSpace(std::string s) {
+  s.erase(std::remove_if(s.begin(), s.end(),
+ [](char c) { return std::isspace(c); }),
+  s.end());
+  return s;
+}
+
+} // namespace
+
+TEST(ExprMutationAnalyzerTest, Trivial) {
+  const auto AST = tooling::buildASTFromCode("void f() { int x; x; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
+class AssignmentTest : public ::testing::TestWithParam {};
+
+TEST_P(AssignmentTest, AssignmentModifies) {
+  const std::string ModExpr = "x " + GetParam() + " 10";
+  const auto AST =
+  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllAssignmentOperators, AssignmentTest,
+Values("=", "+=", "-=", "*=", "/=", "%=", "&=", "|=",
+   "^=", "<<=", ">>="), );
+
+class IncDecTest : public ::testing::TestWithParam {};
+
+TEST_P(IncDecTest, IncDecModifies) {
+  const std::string ModExpr = GetParam();
+  const auto AST =
+  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllIncDecOperators, IncDecTest,
+Values("++x", "--x", "x++", "x--"), );
+
+TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) {
+  const auto AST = tooling::buildASTFromCode(
+  "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+}
+
+TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
+  const auto AST = tooling::buildASTFromCode(
+  "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());

[PATCH] D45679: [clang-tidy] Add ExprMutationAnalyzer, that analyzes whether an expression is mutated within a statement.

2018-06-03 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added inline comments.



Comment at: clang-tidy/utils/ExprMutationAnalyzer.cpp:34-38
+const ast_matchers::internal::VariadicDynCastAllOfMatcher
+cxxTypeidExpr;
+
+const ast_matchers::internal::VariadicDynCastAllOfMatcher
+cxxNoexceptExpr;

aaron.ballman wrote:
> I think these should be exposed as public AST matchers, as they're both 
> generally useful. It can be done in a follow-up patch, or done before landing 
> this patch, either is fine by me.
Will leave as follow up patch.



Comment at: clang-tidy/utils/ExprMutationAnalyzer.cpp:67
+
+bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
+  return selectFirst(

aaron.ballman wrote:
> What about other unevaluated expressions, like `typeof`, `_Generic`, etc? You 
> should search for `ExpressionEvaluationContext::Unevaluated` in Clang to see 
> where we set up an unevaluated expression evaluation context to find all of 
> the situations.
I checked around and I think these are all we care about:
- decltype/typeof
- sizeof/alignof
- noexcept
- _Generic
- typeid

I've added TODO for _Generic and typeid for now because I think those need a 
bit more work to implement, while at the same time not supporting them for now 
only creates false positives from isMutated which is what we prefer over false 
negatives.



Comment at: clang-tidy/utils/ExprMutationAnalyzer.cpp:72
+ findAll(expr(equalsNode(Exp),
+  anyOf(hasAncestor(typeLoc()),
+hasAncestor(expr(anyOf(

aaron.ballman wrote:
> What is this trying to match with the `typeLoc()` matcher?
Added comment to explain.



Comment at: clang-tidy/utils/ExprMutationAnalyzer.cpp:74-75
+hasAncestor(expr(anyOf(
+unaryExprOrTypeTraitExpr(),
+cxxTypeidExpr(), cxxNoexceptExpr())
+ .bind("expr")),

aaron.ballman wrote:
> I think these are only approximations to testing whether it's unevaluated. 
> For instance, won't this match these expressions, despite them being 
> evaluated?
> ```
> // C++
> #include 
> 
> struct B {
>   virtual ~B() = default;
> };
> 
> struct D : B {};
> 
> B& get() {
>   static B *b = new D;
>   return *b;
> }
> 
> void f() {
>   (void)typeid(get()); // Evaluated, calls get()
> }
> 
> /* C99 and later */
> #include 
> 
> void f(size_t n) {
>   (void)sizeof(int[++n]); // Evaluated, increments n
> }
> ```
Fixed handling of sizeof(VLA)
Added TODO for typeid


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45679



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45679: [clang-tidy] Add ExprMutationAnalyzer, that analyzes whether an expression is mutated within a statement.

2018-06-03 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 149661.
shuaiwang marked 4 inline comments as done.
shuaiwang added a comment.

Handle typeid and generic selection.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45679

Files:
  clang-tidy/utils/CMakeLists.txt
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  clang-tidy/utils/ExprMutationAnalyzer.h
  unittests/clang-tidy/CMakeLists.txt
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- /dev/null
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -0,0 +1,608 @@
+//===-- ExprMutationAnalyzerTest.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 "../clang-tidy/utils/ExprMutationAnalyzer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+using namespace clang::ast_matchers;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::ResultOf;
+using ::testing::StartsWith;
+using ::testing::Values;
+
+namespace {
+
+using ExprMatcher = internal::Matcher;
+using StmtMatcher = internal::Matcher;
+
+ExprMatcher declRefTo(StringRef Name) {
+  return declRefExpr(to(namedDecl(hasName(Name;
+}
+
+StmtMatcher withEnclosingCompound(ExprMatcher Matcher) {
+  return expr(Matcher, hasAncestor(compoundStmt().bind("stmt"))).bind("expr");
+}
+
+bool isMutated(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  const auto *const E = selectFirst("expr", Results);
+  return utils::ExprMutationAnalyzer(S, &AST->getASTContext()).isMutated(E);
+}
+
+SmallVector
+mutatedBy(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  SmallVector Chain;
+  utils::ExprMutationAnalyzer Analyzer(S, &AST->getASTContext());
+  for (const auto *E = selectFirst("expr", Results); E != nullptr;) {
+const Stmt *By = Analyzer.findMutation(E);
+std::string buffer;
+llvm::raw_string_ostream stream(buffer);
+By->printPretty(stream, nullptr, AST->getASTContext().getPrintingPolicy());
+Chain.push_back(StringRef(stream.str()).trim().str());
+E = dyn_cast(By);
+  }
+  return Chain;
+}
+
+std::string removeSpace(std::string s) {
+  s.erase(std::remove_if(s.begin(), s.end(),
+ [](char c) { return std::isspace(c); }),
+  s.end());
+  return s;
+}
+
+} // namespace
+
+TEST(ExprMutationAnalyzerTest, Trivial) {
+  const auto AST = tooling::buildASTFromCode("void f() { int x; x; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
+class AssignmentTest : public ::testing::TestWithParam {};
+
+TEST_P(AssignmentTest, AssignmentModifies) {
+  const std::string ModExpr = "x " + GetParam() + " 10";
+  const auto AST =
+  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllAssignmentOperators, AssignmentTest,
+Values("=", "+=", "-=", "*=", "/=", "%=", "&=", "|=",
+   "^=", "<<=", ">>="), );
+
+class IncDecTest : public ::testing::TestWithParam {};
+
+TEST_P(IncDecTest, IncDecModifies) {
+  const std::string ModExpr = GetParam();
+  const auto AST =
+  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllIncDecOperators, IncDecTest,
+Values("++x", "--x", "x++", "x--"), );
+
+TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) {
+  const auto AST = tooling::buildASTFromCode(
+  "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+}
+
+TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
+  const auto AST = tooling::buildASTFromCode(
+  "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
+TEST(ExprMuta

[PATCH] D45679: [clang-tidy] Add ExprMutationAnalyzer, that analyzes whether an expression is mutated within a statement.

2018-06-03 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

Overestimated the work of supporting typeid and _Generic, done now.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45679



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45679: [clang-tidy] Add ExprMutationAnalyzer, that analyzes whether an expression is mutated within a statement.

2018-06-05 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 150026.
shuaiwang marked an inline comment as done.
shuaiwang added a comment.

Remove stale comment


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45679

Files:
  clang-tidy/utils/CMakeLists.txt
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  clang-tidy/utils/ExprMutationAnalyzer.h
  unittests/clang-tidy/CMakeLists.txt
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- /dev/null
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -0,0 +1,608 @@
+//===-- ExprMutationAnalyzerTest.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 "../clang-tidy/utils/ExprMutationAnalyzer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+using namespace clang::ast_matchers;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::ResultOf;
+using ::testing::StartsWith;
+using ::testing::Values;
+
+namespace {
+
+using ExprMatcher = internal::Matcher;
+using StmtMatcher = internal::Matcher;
+
+ExprMatcher declRefTo(StringRef Name) {
+  return declRefExpr(to(namedDecl(hasName(Name;
+}
+
+StmtMatcher withEnclosingCompound(ExprMatcher Matcher) {
+  return expr(Matcher, hasAncestor(compoundStmt().bind("stmt"))).bind("expr");
+}
+
+bool isMutated(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  const auto *const E = selectFirst("expr", Results);
+  return utils::ExprMutationAnalyzer(S, &AST->getASTContext()).isMutated(E);
+}
+
+SmallVector
+mutatedBy(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  SmallVector Chain;
+  utils::ExprMutationAnalyzer Analyzer(S, &AST->getASTContext());
+  for (const auto *E = selectFirst("expr", Results); E != nullptr;) {
+const Stmt *By = Analyzer.findMutation(E);
+std::string buffer;
+llvm::raw_string_ostream stream(buffer);
+By->printPretty(stream, nullptr, AST->getASTContext().getPrintingPolicy());
+Chain.push_back(StringRef(stream.str()).trim().str());
+E = dyn_cast(By);
+  }
+  return Chain;
+}
+
+std::string removeSpace(std::string s) {
+  s.erase(std::remove_if(s.begin(), s.end(),
+ [](char c) { return std::isspace(c); }),
+  s.end());
+  return s;
+}
+
+} // namespace
+
+TEST(ExprMutationAnalyzerTest, Trivial) {
+  const auto AST = tooling::buildASTFromCode("void f() { int x; x; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
+class AssignmentTest : public ::testing::TestWithParam {};
+
+TEST_P(AssignmentTest, AssignmentModifies) {
+  const std::string ModExpr = "x " + GetParam() + " 10";
+  const auto AST =
+  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllAssignmentOperators, AssignmentTest,
+Values("=", "+=", "-=", "*=", "/=", "%=", "&=", "|=",
+   "^=", "<<=", ">>="), );
+
+class IncDecTest : public ::testing::TestWithParam {};
+
+TEST_P(IncDecTest, IncDecModifies) {
+  const std::string ModExpr = GetParam();
+  const auto AST =
+  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllIncDecOperators, IncDecTest,
+Values("++x", "--x", "x++", "x--"), );
+
+TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) {
+  const auto AST = tooling::buildASTFromCode(
+  "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+}
+
+TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
+  const auto AST = tooling::buildASTFromCode(
+  "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
+TEST(ExprMutationAnalyzerTest

[PATCH] D45679: [clang-tidy] Add ExprMutationAnalyzer, that analyzes whether an expression is mutated within a statement.

2018-06-05 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

Thanks a lot for the review!

Could you also help commit this diff as well? Thanks!


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45679



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45679: [clang-tidy] Add ExprMutationAnalyzer, that analyzes whether an expression is mutated within a statement.

2018-06-13 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 151244.
shuaiwang added a comment.

Don't include  in unit tests, should fix the test failures.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45679

Files:
  clang-tidy/utils/CMakeLists.txt
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  clang-tidy/utils/ExprMutationAnalyzer.h
  unittests/clang-tidy/CMakeLists.txt
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- /dev/null
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -0,0 +1,608 @@
+//===-- ExprMutationAnalyzerTest.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 "../clang-tidy/utils/ExprMutationAnalyzer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+using namespace clang::ast_matchers;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::ResultOf;
+using ::testing::StartsWith;
+using ::testing::Values;
+
+namespace {
+
+using ExprMatcher = internal::Matcher;
+using StmtMatcher = internal::Matcher;
+
+ExprMatcher declRefTo(StringRef Name) {
+  return declRefExpr(to(namedDecl(hasName(Name;
+}
+
+StmtMatcher withEnclosingCompound(ExprMatcher Matcher) {
+  return expr(Matcher, hasAncestor(compoundStmt().bind("stmt"))).bind("expr");
+}
+
+bool isMutated(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  const auto *const E = selectFirst("expr", Results);
+  return utils::ExprMutationAnalyzer(S, &AST->getASTContext()).isMutated(E);
+}
+
+SmallVector
+mutatedBy(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  SmallVector Chain;
+  utils::ExprMutationAnalyzer Analyzer(S, &AST->getASTContext());
+  for (const auto *E = selectFirst("expr", Results); E != nullptr;) {
+const Stmt *By = Analyzer.findMutation(E);
+std::string buffer;
+llvm::raw_string_ostream stream(buffer);
+By->printPretty(stream, nullptr, AST->getASTContext().getPrintingPolicy());
+Chain.push_back(StringRef(stream.str()).trim().str());
+E = dyn_cast(By);
+  }
+  return Chain;
+}
+
+std::string removeSpace(std::string s) {
+  s.erase(std::remove_if(s.begin(), s.end(),
+ [](char c) { return std::isspace(c); }),
+  s.end());
+  return s;
+}
+
+} // namespace
+
+TEST(ExprMutationAnalyzerTest, Trivial) {
+  const auto AST = tooling::buildASTFromCode("void f() { int x; x; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
+class AssignmentTest : public ::testing::TestWithParam {};
+
+TEST_P(AssignmentTest, AssignmentModifies) {
+  const std::string ModExpr = "x " + GetParam() + " 10";
+  const auto AST =
+  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllAssignmentOperators, AssignmentTest,
+Values("=", "+=", "-=", "*=", "/=", "%=", "&=", "|=",
+   "^=", "<<=", ">>="), );
+
+class IncDecTest : public ::testing::TestWithParam {};
+
+TEST_P(IncDecTest, IncDecModifies) {
+  const std::string ModExpr = GetParam();
+  const auto AST =
+  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllIncDecOperators, IncDecTest,
+Values("++x", "--x", "x++", "x--"), );
+
+TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) {
+  const auto AST = tooling::buildASTFromCode(
+  "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+}
+
+TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
+  const auto AST = tooling::buildASTFromCode(
+  "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
+TEST(ExprMutationAnalyzerTest, Non

[PATCH] D45679: [clang-tidy] Add ExprMutationAnalyzer, that analyzes whether an expression is mutated within a statement.

2018-06-13 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 151245.
shuaiwang added a comment.

Add include  for std::isspace() (thanks aaron.ballman!)


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45679

Files:
  clang-tidy/utils/CMakeLists.txt
  clang-tidy/utils/ExprMutationAnalyzer.cpp
  clang-tidy/utils/ExprMutationAnalyzer.h
  unittests/clang-tidy/CMakeLists.txt
  unittests/clang-tidy/ExprMutationAnalyzerTest.cpp

Index: unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
===
--- /dev/null
+++ unittests/clang-tidy/ExprMutationAnalyzerTest.cpp
@@ -0,0 +1,609 @@
+//===-- ExprMutationAnalyzerTest.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 "../clang-tidy/utils/ExprMutationAnalyzer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include 
+
+namespace clang {
+namespace tidy {
+namespace test {
+
+using namespace clang::ast_matchers;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::ResultOf;
+using ::testing::StartsWith;
+using ::testing::Values;
+
+namespace {
+
+using ExprMatcher = internal::Matcher;
+using StmtMatcher = internal::Matcher;
+
+ExprMatcher declRefTo(StringRef Name) {
+  return declRefExpr(to(namedDecl(hasName(Name;
+}
+
+StmtMatcher withEnclosingCompound(ExprMatcher Matcher) {
+  return expr(Matcher, hasAncestor(compoundStmt().bind("stmt"))).bind("expr");
+}
+
+bool isMutated(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  const auto *const E = selectFirst("expr", Results);
+  return utils::ExprMutationAnalyzer(S, &AST->getASTContext()).isMutated(E);
+}
+
+SmallVector
+mutatedBy(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  SmallVector Chain;
+  utils::ExprMutationAnalyzer Analyzer(S, &AST->getASTContext());
+  for (const auto *E = selectFirst("expr", Results); E != nullptr;) {
+const Stmt *By = Analyzer.findMutation(E);
+std::string buffer;
+llvm::raw_string_ostream stream(buffer);
+By->printPretty(stream, nullptr, AST->getASTContext().getPrintingPolicy());
+Chain.push_back(StringRef(stream.str()).trim().str());
+E = dyn_cast(By);
+  }
+  return Chain;
+}
+
+std::string removeSpace(std::string s) {
+  s.erase(std::remove_if(s.begin(), s.end(),
+ [](char c) { return std::isspace(c); }),
+  s.end());
+  return s;
+}
+
+} // namespace
+
+TEST(ExprMutationAnalyzerTest, Trivial) {
+  const auto AST = tooling::buildASTFromCode("void f() { int x; x; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
+class AssignmentTest : public ::testing::TestWithParam {};
+
+TEST_P(AssignmentTest, AssignmentModifies) {
+  const std::string ModExpr = "x " + GetParam() + " 10";
+  const auto AST =
+  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllAssignmentOperators, AssignmentTest,
+Values("=", "+=", "-=", "*=", "/=", "%=", "&=", "|=",
+   "^=", "<<=", ">>="), );
+
+class IncDecTest : public ::testing::TestWithParam {};
+
+TEST_P(IncDecTest, IncDecModifies) {
+  const std::string ModExpr = GetParam();
+  const auto AST =
+  tooling::buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllIncDecOperators, IncDecTest,
+Values("++x", "--x", "x++", "x--"), );
+
+TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) {
+  const auto AST = tooling::buildASTFromCode(
+  "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+}
+
+TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
+  const auto AST = tooling::buildASTFromCode(
+  "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
+TEST(ExprMutationAnalyzerTe

[PATCH] D45679: [clang-tidy] Add ExprMutationAnalyzer, that analyzes whether an expression is mutated within a statement.

2018-06-13 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D45679#1131115, @aaron.ballman wrote:

> I had to revert due to failing tests. The revert was done in r334606 and this 
> is an example of a failing bot: 
> http://lab.llvm.org:8011/builders/llvm-clang-lld-x86_64-scei-ps4-ubuntu-fast/builds/31500


Apparently I can't include  in unit test cases (somehow that works on 
my machine :p)
Changed to just forward declare std::type_info instead.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D45679



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D54399: Move ExprMutationAnalyzer to Tooling/Analysis (1/3)

2019-01-23 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

Friendly ping @EricWF


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D54399/new/

https://reviews.llvm.org/D54399



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D54399: Move ExprMutationAnalyzer to Tooling/Analysis (1/3)

2018-11-11 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
Herald added subscribers: cfe-commits, dkrupp, donat.nagy, Szelethus, 
a.sidorin, mgorny.
Herald added a reviewer: george.karpenkov.

This just copies ExprMutationAnalyzer to Tooling/Analysis with minor tweaks 
around including path & namespaces.
2/3 will change existing references to old location to new location.
3/3 will delete the old copy.


Repository:
  rC Clang

https://reviews.llvm.org/D54399

Files:
  include/clang/Tooling/Analysis/ExprMutationAnalyzer.h
  lib/Tooling/Analysis/CMakeLists.txt
  lib/Tooling/Analysis/ExprMutationAnalyzer.cpp
  lib/Tooling/CMakeLists.txt
  unittests/Tooling/CMakeLists.txt
  unittests/Tooling/ExprMutationAnalyzerTest.cpp

Index: unittests/Tooling/ExprMutationAnalyzerTest.cpp
===
--- /dev/null
+++ unittests/Tooling/ExprMutationAnalyzerTest.cpp
@@ -0,0 +1,1107 @@
+//===-- ExprMutationAnalyzerTest.cpp --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--===//
+
+#include "clang/Tooling/Analysis/ExprMutationAnalyzer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/Tooling.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include 
+
+namespace clang {
+namespace {
+
+using namespace ::clang::ast_matchers;
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::ResultOf;
+using ::testing::StartsWith;
+using ::testing::Values;
+
+using ExprMatcher = internal::Matcher;
+using StmtMatcher = internal::Matcher;
+
+std::unique_ptr
+buildASTFromCodeWithArgs(const Twine &Code,
+ const std::vector &Args) {
+  auto AST = tooling::buildASTFromCodeWithArgs(Code, Args);
+  EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred());
+  return AST;
+}
+
+std::unique_ptr buildASTFromCode(const Twine &Code) {
+  return buildASTFromCodeWithArgs(Code, {});
+}
+
+ExprMatcher declRefTo(StringRef Name) {
+  return declRefExpr(to(namedDecl(hasName(Name;
+}
+
+StmtMatcher withEnclosingCompound(ExprMatcher Matcher) {
+  return expr(Matcher, hasAncestor(compoundStmt().bind("stmt"))).bind("expr");
+}
+
+bool isMutated(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  const auto *const E = selectFirst("expr", Results);
+  return tooling::ExprMutationAnalyzer(*S, AST->getASTContext()).isMutated(E);
+}
+
+SmallVector
+mutatedBy(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  SmallVector Chain;
+  tooling::ExprMutationAnalyzer Analyzer(*S, AST->getASTContext());
+  for (const auto *E = selectFirst("expr", Results); E != nullptr;) {
+const Stmt *By = Analyzer.findMutation(E);
+std::string buffer;
+llvm::raw_string_ostream stream(buffer);
+By->printPretty(stream, nullptr, AST->getASTContext().getPrintingPolicy());
+Chain.push_back(StringRef(stream.str()).trim().str());
+E = dyn_cast(By);
+  }
+  return Chain;
+}
+
+std::string removeSpace(std::string s) {
+  s.erase(std::remove_if(s.begin(), s.end(),
+ [](char c) { return std::isspace(c); }),
+  s.end());
+  return s;
+}
+
+const std::string StdRemoveReference =
+"namespace std {"
+"template struct remove_reference { typedef T type; };"
+"template struct remove_reference { typedef T type; };"
+"template struct remove_reference { typedef T type; }; }";
+
+const std::string StdMove =
+"namespace std {"
+"template typename remove_reference::type&& "
+"move(T&& t) noexcept {"
+"return static_cast::type&&>(t); } }";
+
+const std::string StdForward =
+"namespace std {"
+"template T&& "
+"forward(typename remove_reference::type& t) noexcept { return t; }"
+"template T&& "
+"forward(typename remove_reference::type&& t) noexcept { return t; } }";
+
+TEST(ExprMutationAnalyzerTest, Trivial) {
+  const auto AST = buildASTFromCode("void f() { int x; x; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
+class AssignmentTest : public ::testing::TestWithParam {};
+
+TEST_P(AssignmentTest, AssignmentModifies) {
+  const std::string ModExpr = "x " + GetParam() + " 10";
+  const auto AST = buildASTFromCode("void f() { int x; " + ModExpr + "; }");
+  const auto Results =
+  match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr));
+}
+
+INSTANTIATE_TEST_CASE_P(AllAssignmentOperators, AssignmentTest,
+Values("=", "+=", "-=", "*=", "/=", "%=", "&=", "|=",
+   "^=

[PATCH] D54400: Move ExprMutationAnalyzer to Tooling/Analysis (2/3)

2018-11-11 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
shuaiwang added reviewers: george.karpenkov, rsmith, dblaikie.
Herald added subscribers: cfe-commits, dkrupp, donat.nagy, Szelethus, 
a.sidorin, mgorny.
shuaiwang added a dependency: D54399: Move ExprMutationAnalyzer to 
Tooling/Analysis (1/3).

Reference the new location of ExprMutationAnalyzer.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D54400

Files:
  clang-tidy/performance/CMakeLists.txt
  clang-tidy/performance/ForRangeCopyCheck.cpp
  clang-tidy/performance/UnnecessaryValueParamCheck.cpp
  clang-tidy/performance/UnnecessaryValueParamCheck.h


Index: clang-tidy/performance/UnnecessaryValueParamCheck.h
===
--- clang-tidy/performance/UnnecessaryValueParamCheck.h
+++ clang-tidy/performance/UnnecessaryValueParamCheck.h
@@ -12,7 +12,7 @@
 
 #include "../ClangTidy.h"
 #include "../utils/IncludeInserter.h"
-#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/Tooling/Analysis/ExprMutationAnalyzer.h"
 
 namespace clang {
 namespace tidy {
@@ -36,7 +36,7 @@
   void handleMoveFix(const ParmVarDecl &Var, const DeclRefExpr &CopyArgument,
  const ASTContext &Context);
 
-  llvm::DenseMap
+  llvm::DenseMap
   MutationAnalyzers;
   std::unique_ptr Inserter;
   const utils::IncludeSorter::IncludeStyle IncludeStyle;
Index: clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===
--- clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -97,7 +97,7 @@
   const auto *Param = Result.Nodes.getNodeAs("param");
   const auto *Function = Result.Nodes.getNodeAs("functionDecl");
 
-  FunctionParmMutationAnalyzer &Analyzer =
+  tooling::FunctionParmMutationAnalyzer &Analyzer =
   MutationAnalyzers.try_emplace(Function, *Function, *Result.Context)
   .first->second;
   if (Analyzer.isMutated(Param))
Index: clang-tidy/performance/ForRangeCopyCheck.cpp
===
--- clang-tidy/performance/ForRangeCopyCheck.cpp
+++ clang-tidy/performance/ForRangeCopyCheck.cpp
@@ -13,7 +13,7 @@
 #include "../utils/Matchers.h"
 #include "../utils/OptionsUtils.h"
 #include "../utils/TypeTraits.h"
-#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/Tooling/Analysis/ExprMutationAnalyzer.h"
 
 using namespace clang::ast_matchers;
 
@@ -98,7 +98,8 @@
   // Because the fix (changing to `const auto &`) will introduce an unused
   // compiler warning which can't be suppressed.
   // Since this case is very rare, it is safe to ignore it.
-  if (!ExprMutationAnalyzer(*ForRange.getBody(), Context).isMutated(&LoopVar) 
&&
+  if (!tooling::ExprMutationAnalyzer(*ForRange.getBody(), Context)
+   .isMutated(&LoopVar) &&
   !utils::decl_ref_expr::allDeclRefExprs(LoopVar, *ForRange.getBody(),
  Context)
.empty()) {
Index: clang-tidy/performance/CMakeLists.txt
===
--- clang-tidy/performance/CMakeLists.txt
+++ clang-tidy/performance/CMakeLists.txt
@@ -18,9 +18,9 @@
   LINK_LIBS
   clangAST
   clangASTMatchers
-  clangAnalysis
   clangBasic
   clangLex
   clangTidy
   clangTidyUtils
+  clangToolingAnalysis
   )


Index: clang-tidy/performance/UnnecessaryValueParamCheck.h
===
--- clang-tidy/performance/UnnecessaryValueParamCheck.h
+++ clang-tidy/performance/UnnecessaryValueParamCheck.h
@@ -12,7 +12,7 @@
 
 #include "../ClangTidy.h"
 #include "../utils/IncludeInserter.h"
-#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/Tooling/Analysis/ExprMutationAnalyzer.h"
 
 namespace clang {
 namespace tidy {
@@ -36,7 +36,7 @@
   void handleMoveFix(const ParmVarDecl &Var, const DeclRefExpr &CopyArgument,
  const ASTContext &Context);
 
-  llvm::DenseMap
+  llvm::DenseMap
   MutationAnalyzers;
   std::unique_ptr Inserter;
   const utils::IncludeSorter::IncludeStyle IncludeStyle;
Index: clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===
--- clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -97,7 +97,7 @@
   const auto *Param = Result.Nodes.getNodeAs("param");
   const auto *Function = Result.Nodes.getNodeAs("functionDecl");
 
-  FunctionParmMutationAnalyzer &Analyzer =
+  tooling::FunctionParmMutationAnalyzer &Analyzer =
   MutationAnalyzers.try_emplace(Function, *Function, *Result.Context)
   .first->second;
   if (Analyzer.isMutated(Param))
Index: clang-tidy/performance/ForRangeCopyCheck.cpp
===
--- clang-tidy/performance/ForRangeCopyCheck.cpp
+++ clang-tid

[PATCH] D54399: Move ExprMutationAnalyzer to Tooling/Analysis (1/3)

2018-11-23 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D54399#1296317, @dblaikie wrote:

> Could you fix the modulemap (might amount to reverting the change Eric made 
> in r342827? or maybe it's more involved than that) & validate that the 
> modules build is working with this change (probably undo Eric's change, 
> validate that you see the breakage that Eric was trying to fix, then apply 
> your change & see if it clears up as well)?


I'm having trouble reproducing the failure.
@EricWF, could you help provide the exact build commands you use that can 
reproduce the failure?


Repository:
  rC Clang

https://reviews.llvm.org/D54399



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D52120: [analyzer] Treat std::{move, forward} as casts in ExprMutationAnalyzer.

2018-09-16 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang marked 2 inline comments as done.
shuaiwang added inline comments.



Comment at: unittests/Analysis/ExprMutationAnalyzerTest.cpp:387
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x)"));
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+

JonasToth wrote:
> JonasToth wrote:
> > I am thinking about the correctness of the change.
> > 
> > If we have this case:
> > ```
> > std::string s1 = "Hello World!";
> > std::string s2 = std::move(s1);
> > ```
> > `s1` gets mutated, but it looks like it would not be considered as mutation?
> > 
> > On the other hand
> > ```
> > int i1 = 42;
> > int i2 = std::move(i1);
> > ```
> > should resolve in a copy `i1` and therefor not be a mutation.
> > 
> > Could you please add tests demonstrating this difference and the correct 
> > diagnostic detection for that.
> > 
> > Potentially interesting:
> > - Builtin types should be Copy-Only?, builtin arrays as expensive to move 
> > types for the completeness please as well
> > - Types with deleted move operations, should resolve in copy too
> > - Move-Only types like unique_ptr
> > - Types with both move and copy constructor (vector-like)
> > - and something with everything defaulted
> > 
> > These thoughts are just thinking and writing, I just wondered that moving a 
> > variable with std::move is not considered as mutation here, even though 
> > there are cases were it is actually a mutation.
> Related: 
> https://clang.llvm.org/extra/clang-tidy/checks/performance-move-const-arg.html
Added these tests:
* std::move copy only type doesn't mutate
* std::move move only type does mutate
* std::move copiable & movable type does mutate
* std::move const copiable & movable type doesn't mutate
Didn't test array because array can't be assigned (so copy/move isn't relevant) 
and passing array to function can only be done via reference (passing by value 
results in array to pointer decay) so no copy/move there as well.


Repository:
  rC Clang

https://reviews.llvm.org/D52120



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D52120: [analyzer] Treat std::{move, forward} as casts in ExprMutationAnalyzer.

2018-09-16 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 165684.
shuaiwang marked an inline comment as done.
shuaiwang added a comment.

Added more test cases around std::move


Repository:
  rC Clang

https://reviews.llvm.org/D52120

Files:
  lib/Analysis/ExprMutationAnalyzer.cpp
  unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: unittests/Analysis/ExprMutationAnalyzerTest.cpp
===
--- unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -66,6 +66,25 @@
   return s;
 }
 
+const std::string StdRemoveReference =
+"namespace std {"
+"template struct remove_reference { typedef T type; };"
+"template struct remove_reference { typedef T type; };"
+"template struct remove_reference { typedef T type; }; }";
+
+const std::string StdMove =
+"namespace std {"
+"template typename remove_reference::type&& "
+"move(T&& t) noexcept {"
+"return static_cast::type&&>(t); } }";
+
+const std::string StdForward =
+"namespace std {"
+"template T&& "
+"forward(typename remove_reference::type& t) noexcept { return t; }"
+"template T&& "
+"forward(typename remove_reference::type&&) noexcept { return t; } }";
+
 } // namespace
 
 TEST(ExprMutationAnalyzerTest, Trivial) {
@@ -373,36 +392,81 @@
 }
 
 TEST(ExprMutationAnalyzerTest, Move) {
-  // Technically almost the same as ByNonConstRRefArgument, just double checking
-  const auto AST = tooling::buildASTFromCode(
-  "namespace std {"
-  "template struct remove_reference { typedef T type; };"
-  "template struct remove_reference { typedef T type; };"
-  "template struct remove_reference { typedef T type; };"
-  "template typename std::remove_reference::type&& "
-  "move(T&& t) noexcept; }"
-  "void f() { struct A {}; A x; std::move(x); }");
-  const auto Results =
+  auto AST =
+  tooling::buildASTFromCode(StdRemoveReference + StdMove +
+"void f() { struct A {}; A x; std::move(x); }");
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x)"));
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  StdRemoveReference + StdMove +
+  "void f() { struct A {}; A x, y; std::move(x) = y; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x) = y"));
+
+  AST = tooling::buildASTFromCode(StdRemoveReference + StdMove +
+  "void f() { int x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  StdRemoveReference + StdMove +
+  "struct S { S(); S(const S&); S& operator=(const S&); };"
+  "void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST =
+  tooling::buildASTFromCode(StdRemoveReference + StdMove +
+"struct S { S(); S(S&&); S& operator=(S&&); };"
+"void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)"));
+
+  AST =
+  tooling::buildASTFromCode(StdRemoveReference + StdMove +
+"struct S { S(); S(const S&); S(S&&);"
+"S& operator=(const S&); S& operator=(S&&); };"
+"void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)"));
+
+  AST = tooling::buildASTFromCode(
+  StdRemoveReference + StdMove +
+  "struct S { S(); S(const S&); S(S&&);"
+  "S& operator=(const S&); S& operator=(S&&); };"
+  "void f() { const S x; S y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  StdRemoveReference + StdMove +
+  "struct S{}; void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)"));
+
+  AST = tooling::buildASTFromCode(
+  StdRemoveReference + StdMove +
+  "struct S{}; void f() { const S x; S y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
 TEST(ExprMutationAnaly

[PATCH] D52157: [ASTMatchers] Let isArrow also support UnresolvedMemberExpr, CXXDependentScopeMemberExpr

2018-09-16 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
shuaiwang added a reviewer: aaron.ballman.
Herald added a subscriber: cfe-commits.

Repository:
  rC Clang

https://reviews.llvm.org/D52157

Files:
  docs/LibASTMatchersReference.html
  include/clang/ASTMatchers/ASTMatchers.h
  unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
  unittests/ASTMatchers/ASTMatchersNodeTest.cpp
  unittests/ASTMatchers/Dynamic/RegistryTest.cpp

Index: unittests/ASTMatchers/Dynamic/RegistryTest.cpp
===
--- unittests/ASTMatchers/Dynamic/RegistryTest.cpp
+++ unittests/ASTMatchers/Dynamic/RegistryTest.cpp
@@ -440,7 +440,8 @@
   Error.get()).isNull());
   EXPECT_EQ("Incorrect type for arg 1. "
 "(Expected = Matcher) != "
-"(Actual = Matcher&Matcher)",
+"(Actual = Matcher&Matcher"
+")",
 Error->toString());
 }
 
Index: unittests/ASTMatchers/ASTMatchersNodeTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -27,7 +27,7 @@
nullptr));
 
   // Do not accept non-toplevel matchers.
-  EXPECT_FALSE(Finder.addDynamicMatcher(isArrow(), nullptr));
+  EXPECT_FALSE(Finder.addDynamicMatcher(isMain(), nullptr));
   EXPECT_FALSE(Finder.addDynamicMatcher(hasName("x"), nullptr));
 }
 
Index: unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -765,6 +765,11 @@
   memberExpr(isArrow(;
   EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } int y; };",
  memberExpr(isArrow(;
+  EXPECT_TRUE(matches("template  class Y { void x() { this->m; } };",
+  cxxDependentScopeMemberExpr(isArrow(;
+  EXPECT_TRUE(
+  notMatches("template  class Y { void x() { (*this).m; } };",
+ cxxDependentScopeMemberExpr(isArrow(;
 }
 
 TEST(IsArrow, MatchesStaticMemberVariablesViaArrow) {
@@ -783,6 +788,14 @@
   memberExpr(isArrow(;
   EXPECT_TRUE(notMatches("class Y { void x() { Y y; y.x(); } };",
  memberExpr(isArrow(;
+  EXPECT_TRUE(
+  matches("class Y { template  void x() { this->x(); } };",
+  unresolvedMemberExpr(isArrow(;
+  EXPECT_TRUE(matches("class Y { template  void x() { x(); } };",
+  unresolvedMemberExpr(isArrow(;
+  EXPECT_TRUE(
+  notMatches("class Y { template  void x() { (*this).x(); } };",
+ unresolvedMemberExpr(isArrow(;
 }
 
 TEST(ConversionDeclaration, IsExplicit) {
Index: include/clang/ASTMatchers/ASTMatchers.h
===
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -4703,7 +4703,9 @@
 /// \endcode
 /// memberExpr(isArrow())
 ///   matches this->x, x, y.x, a, this->b
-AST_MATCHER(MemberExpr, isArrow) {
+AST_POLYMORPHIC_MATCHER(
+isArrow, AST_POLYMORPHIC_SUPPORTED_TYPES(MemberExpr, UnresolvedMemberExpr,
+ CXXDependentScopeMemberExpr)) {
   return Node.isArrow();
 }
 
Index: docs/LibASTMatchersReference.html
===
--- docs/LibASTMatchersReference.html
+++ docs/LibASTMatchersReference.html
@@ -2235,6 +2235,23 @@
 
 
 
+MatcherCXXDependentScopeMemberExpr>isArrow
+Matches member expressions that are called with '->' as opposed
+to '.'.
+
+Member calls on the implicit this pointer match as called with '->'.
+
+Given
+  class Y {
+void x() { this->x(); x(); Y y; y.x(); a; this->b; Y::b; }
+int a;
+static int b;
+  };
+memberExpr(isArrow())
+  matches this->x, x, y.x, a, this->b
+
+
+
 MatcherCXXMethodDecl>isConst
 Matches if the given method declaration is const.
 
@@ -3886,6 +3903,23 @@
 
 
 
+MatcherUnresolvedMemberExpr>isArrow
+Matches member expressions that are called with '->' as opposed
+to '.'.
+
+Member calls on the implicit this pointer match as called with '->'.
+
+Given
+  class Y {
+void x() { this->x(); x(); Y y; y.x(); a; this->b; Y::b; }
+int a;
+static int b;
+  };
+memberExpr(isArrow())
+  matches this->x, x, y.x, a, this->b
+
+
+
 MatcherVarDecl>hasAutomaticStorageDuration
 Matches a variable declaration that has automatic storage duration.
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-b

[PATCH] D52158: [clang-tidy] Remove duplicated logic in UnnecessaryValueParamCheck and use FunctionParmMutationAnalyzer instead.

2018-09-16 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
shuaiwang added reviewers: alexfh, JonasToth.
Herald added subscribers: cfe-commits, Szelethus, a.sidorin, chrib, 
kristof.beyls, xazax.hun.
Herald added a reviewer: george.karpenkov.

Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D52158

Files:
  clang-tidy/performance/UnnecessaryValueParamCheck.cpp
  clang-tidy/performance/UnnecessaryValueParamCheck.h


Index: clang-tidy/performance/UnnecessaryValueParamCheck.h
===
--- clang-tidy/performance/UnnecessaryValueParamCheck.h
+++ clang-tidy/performance/UnnecessaryValueParamCheck.h
@@ -12,6 +12,7 @@
 
 #include "../ClangTidy.h"
 #include "../utils/IncludeInserter.h"
+#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
 
 namespace clang {
 namespace tidy {
@@ -29,11 +30,14 @@
   void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
   void registerPPCallbacks(CompilerInstance &Compiler) override;
   void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void onEndOfTranslationUnit() override;
 
 private:
   void handleMoveFix(const ParmVarDecl &Var, const DeclRefExpr &CopyArgument,
  const ASTContext &Context);
 
+  llvm::DenseMap
+  MutationAnalyzers;
   std::unique_ptr Inserter;
   const utils::IncludeSorter::IncludeStyle IncludeStyle;
 };
Index: clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===
--- clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -13,7 +13,6 @@
 #include "../utils/FixItHintUtils.h"
 #include "../utils/Matchers.h"
 #include "../utils/TypeTraits.h"
-#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Lex/Lexer.h"
 #include "clang/Lex/Preprocessor.h"
@@ -92,21 +91,11 @@
   const auto *Param = Result.Nodes.getNodeAs("param");
   const auto *Function = Result.Nodes.getNodeAs("functionDecl");
 
-  // Do not trigger on non-const value parameters when they are mutated either
-  // within the function body or within init expression(s) when the function is
-  // a ctor.
-  if (ExprMutationAnalyzer(*Function->getBody(), *Result.Context)
-  .isMutated(Param))
+  FunctionParmMutationAnalyzer &Analyzer =
+  MutationAnalyzers.try_emplace(Function, *Function, *Result.Context)
+  .first->second;
+  if (Analyzer.isMutated(Param))
 return;
-  // CXXCtorInitializer might also mutate Param but they're not part of 
function
-  // body, so check them separately here.
-  if (const auto *Ctor = dyn_cast(Function)) {
-for (const auto *Init : Ctor->inits()) {
-  if (ExprMutationAnalyzer(*Init->getInit(), *Result.Context)
-  .isMutated(Param))
-return;
-}
-  }
 
   const bool IsConstQualified =
   Param->getType().getCanonicalType().isConstQualified();
@@ -186,6 +175,10 @@
 utils::IncludeSorter::toString(IncludeStyle));
 }
 
+void UnnecessaryValueParamCheck::onEndOfTranslationUnit() {
+  MutationAnalyzers.clear();
+}
+
 void UnnecessaryValueParamCheck::handleMoveFix(const ParmVarDecl &Var,
const DeclRefExpr &CopyArgument,
const ASTContext &Context) {


Index: clang-tidy/performance/UnnecessaryValueParamCheck.h
===
--- clang-tidy/performance/UnnecessaryValueParamCheck.h
+++ clang-tidy/performance/UnnecessaryValueParamCheck.h
@@ -12,6 +12,7 @@
 
 #include "../ClangTidy.h"
 #include "../utils/IncludeInserter.h"
+#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
 
 namespace clang {
 namespace tidy {
@@ -29,11 +30,14 @@
   void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
   void registerPPCallbacks(CompilerInstance &Compiler) override;
   void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void onEndOfTranslationUnit() override;
 
 private:
   void handleMoveFix(const ParmVarDecl &Var, const DeclRefExpr &CopyArgument,
  const ASTContext &Context);
 
+  llvm::DenseMap
+  MutationAnalyzers;
   std::unique_ptr Inserter;
   const utils::IncludeSorter::IncludeStyle IncludeStyle;
 };
Index: clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===
--- clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -13,7 +13,6 @@
 #include "../utils/FixItHintUtils.h"
 #include "../utils/Matchers.h"
 #include "../utils/TypeTraits.h"
-#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Lex/Lexer.h"
 #include "clang/Lex/Preprocessor.h"
@@ -92,21 +91,11 @@
   const auto *Param = Result.Nodes.getNodeAs("param");
   const auto 

[PATCH] D52158: [clang-tidy] Remove duplicated logic in UnnecessaryValueParamCheck and use FunctionParmMutationAnalyzer instead.

2018-09-17 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL342403: [clang-tidy] Remove duplicated logic in 
UnnecessaryValueParamCheck and use… (authored by shuaiwang, committed by ).
Herald added a subscriber: llvm-commits.

Repository:
  rL LLVM

https://reviews.llvm.org/D52158

Files:
  clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
  clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.h


Index: 
clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.h
===
--- clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.h
+++ clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.h
@@ -12,6 +12,7 @@
 
 #include "../ClangTidy.h"
 #include "../utils/IncludeInserter.h"
+#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
 
 namespace clang {
 namespace tidy {
@@ -29,11 +30,14 @@
   void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
   void registerPPCallbacks(CompilerInstance &Compiler) override;
   void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void onEndOfTranslationUnit() override;
 
 private:
   void handleMoveFix(const ParmVarDecl &Var, const DeclRefExpr &CopyArgument,
  const ASTContext &Context);
 
+  llvm::DenseMap
+  MutationAnalyzers;
   std::unique_ptr Inserter;
   const utils::IncludeSorter::IncludeStyle IncludeStyle;
 };
Index: 
clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===
--- 
clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ 
clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -13,7 +13,6 @@
 #include "../utils/FixItHintUtils.h"
 #include "../utils/Matchers.h"
 #include "../utils/TypeTraits.h"
-#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Lex/Lexer.h"
 #include "clang/Lex/Preprocessor.h"
@@ -92,21 +91,11 @@
   const auto *Param = Result.Nodes.getNodeAs("param");
   const auto *Function = Result.Nodes.getNodeAs("functionDecl");
 
-  // Do not trigger on non-const value parameters when they are mutated either
-  // within the function body or within init expression(s) when the function is
-  // a ctor.
-  if (ExprMutationAnalyzer(*Function->getBody(), *Result.Context)
-  .isMutated(Param))
+  FunctionParmMutationAnalyzer &Analyzer =
+  MutationAnalyzers.try_emplace(Function, *Function, *Result.Context)
+  .first->second;
+  if (Analyzer.isMutated(Param))
 return;
-  // CXXCtorInitializer might also mutate Param but they're not part of 
function
-  // body, so check them separately here.
-  if (const auto *Ctor = dyn_cast(Function)) {
-for (const auto *Init : Ctor->inits()) {
-  if (ExprMutationAnalyzer(*Init->getInit(), *Result.Context)
-  .isMutated(Param))
-return;
-}
-  }
 
   const bool IsConstQualified =
   Param->getType().getCanonicalType().isConstQualified();
@@ -186,6 +175,10 @@
 utils::IncludeSorter::toString(IncludeStyle));
 }
 
+void UnnecessaryValueParamCheck::onEndOfTranslationUnit() {
+  MutationAnalyzers.clear();
+}
+
 void UnnecessaryValueParamCheck::handleMoveFix(const ParmVarDecl &Var,
const DeclRefExpr &CopyArgument,
const ASTContext &Context) {


Index: clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.h
===
--- clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.h
+++ clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.h
@@ -12,6 +12,7 @@
 
 #include "../ClangTidy.h"
 #include "../utils/IncludeInserter.h"
+#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
 
 namespace clang {
 namespace tidy {
@@ -29,11 +30,14 @@
   void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
   void registerPPCallbacks(CompilerInstance &Compiler) override;
   void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void onEndOfTranslationUnit() override;
 
 private:
   void handleMoveFix(const ParmVarDecl &Var, const DeclRefExpr &CopyArgument,
  const ASTContext &Context);
 
+  llvm::DenseMap
+  MutationAnalyzers;
   std::unique_ptr Inserter;
   const utils::IncludeSorter::IncludeStyle IncludeStyle;
 };
Index: clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
===
--- clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
+++ clang-tools-extra/trunk/clang-tidy/performance/UnnecessaryValueParamCheck.cpp
@@ -13,7 +13,6 @@

[PATCH] D52157: [ASTMatchers] Let isArrow also support UnresolvedMemberExpr, CXXDependentScopeMemberExpr

2018-09-17 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 165799.
shuaiwang marked an inline comment as done.
shuaiwang added a comment.

Addressed review comment.


Repository:
  rC Clang

https://reviews.llvm.org/D52157

Files:
  docs/LibASTMatchersReference.html
  include/clang/ASTMatchers/ASTMatchers.h
  unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
  unittests/ASTMatchers/ASTMatchersNodeTest.cpp
  unittests/ASTMatchers/Dynamic/RegistryTest.cpp

Index: unittests/ASTMatchers/Dynamic/RegistryTest.cpp
===
--- unittests/ASTMatchers/Dynamic/RegistryTest.cpp
+++ unittests/ASTMatchers/Dynamic/RegistryTest.cpp
@@ -440,7 +440,8 @@
   Error.get()).isNull());
   EXPECT_EQ("Incorrect type for arg 1. "
 "(Expected = Matcher) != "
-"(Actual = Matcher&Matcher)",
+"(Actual = Matcher&Matcher"
+")",
 Error->toString());
 }
 
Index: unittests/ASTMatchers/ASTMatchersNodeTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -27,7 +27,7 @@
nullptr));
 
   // Do not accept non-toplevel matchers.
-  EXPECT_FALSE(Finder.addDynamicMatcher(isArrow(), nullptr));
+  EXPECT_FALSE(Finder.addDynamicMatcher(isMain(), nullptr));
   EXPECT_FALSE(Finder.addDynamicMatcher(hasName("x"), nullptr));
 }
 
Index: unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -765,6 +765,11 @@
   memberExpr(isArrow(;
   EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } int y; };",
  memberExpr(isArrow(;
+  EXPECT_TRUE(matches("template  class Y { void x() { this->m; } };",
+  cxxDependentScopeMemberExpr(isArrow(;
+  EXPECT_TRUE(
+  notMatches("template  class Y { void x() { (*this).m; } };",
+ cxxDependentScopeMemberExpr(isArrow(;
 }
 
 TEST(IsArrow, MatchesStaticMemberVariablesViaArrow) {
@@ -783,6 +788,14 @@
   memberExpr(isArrow(;
   EXPECT_TRUE(notMatches("class Y { void x() { Y y; y.x(); } };",
  memberExpr(isArrow(;
+  EXPECT_TRUE(
+  matches("class Y { template  void x() { this->x(); } };",
+  unresolvedMemberExpr(isArrow(;
+  EXPECT_TRUE(matches("class Y { template  void x() { x(); } };",
+  unresolvedMemberExpr(isArrow(;
+  EXPECT_TRUE(
+  notMatches("class Y { template  void x() { (*this).x(); } };",
+ unresolvedMemberExpr(isArrow(;
 }
 
 TEST(ConversionDeclaration, IsExplicit) {
Index: include/clang/ASTMatchers/ASTMatchers.h
===
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -4697,13 +4697,24 @@
 /// \code
 ///   class Y {
 /// void x() { this->x(); x(); Y y; y.x(); a; this->b; Y::b; }
+/// template  void f() { this->f(); f(); }
 /// int a;
 /// static int b;
 ///   };
+///   template 
+///   class Z {
+/// void x() { this->m; }
+///   };
 /// \endcode
 /// memberExpr(isArrow())
 ///   matches this->x, x, y.x, a, this->b
-AST_MATCHER(MemberExpr, isArrow) {
+/// cxxDependentScopeMemberExpr(isArrow())
+///   matches this->m
+/// unresolvedMemberExpr(isArrow())
+///   matches this->f, f
+AST_POLYMORPHIC_MATCHER(
+isArrow, AST_POLYMORPHIC_SUPPORTED_TYPES(MemberExpr, UnresolvedMemberExpr,
+ CXXDependentScopeMemberExpr)) {
   return Node.isArrow();
 }
 
Index: docs/LibASTMatchersReference.html
===
--- docs/LibASTMatchersReference.html
+++ docs/LibASTMatchersReference.html
@@ -2235,6 +2235,32 @@
 
 
 
+MatcherCXXDependentScopeMemberExpr>isArrow
+Matches member expressions that are called with '->' as opposed
+to '.'.
+
+Member calls on the implicit this pointer match as called with '->'.
+
+Given
+  class Y {
+void x() { this->x(); x(); Y y; y.x(); a; this->b; Y::b; }
+template  void f() { this->f(); f(); }
+int a;
+static int b;
+  };
+  template 
+  class Z {
+void x() { this->m; }
+  };
+memberExpr(isArrow())
+  matches this->x, x, y.x, a, this->b
+cxxDependentScopeMemberExpr(isArrow())
+  matches this->m
+unresolvedMemberExpr(isArrow())
+  matches this->f, f
+
+
+
 MatcherCXXMethodDecl>isConst
 Matches if the given method declaration is const.
 
@@ -3228,11 +3254,20 @@
 Given
   class Y {
 void x() { this->x(); x(); Y y; y.x(); a; this->b; 

[PATCH] D52157: [ASTMatchers] Let isArrow also support UnresolvedMemberExpr, CXXDependentScopeMemberExpr

2018-09-17 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL342407: [ASTMatchers] Let isArrow also support 
UnresolvedMemberExpr… (authored by shuaiwang, committed by ).
Herald added a subscriber: llvm-commits.

Repository:
  rL LLVM

https://reviews.llvm.org/D52157

Files:
  cfe/trunk/docs/LibASTMatchersReference.html
  cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
  cfe/trunk/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
  cfe/trunk/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
  cfe/trunk/unittests/ASTMatchers/Dynamic/RegistryTest.cpp

Index: cfe/trunk/docs/LibASTMatchersReference.html
===
--- cfe/trunk/docs/LibASTMatchersReference.html
+++ cfe/trunk/docs/LibASTMatchersReference.html
@@ -2235,6 +2235,32 @@
 
 
 
+MatcherCXXDependentScopeMemberExpr>isArrow
+Matches member expressions that are called with '->' as opposed
+to '.'.
+
+Member calls on the implicit this pointer match as called with '->'.
+
+Given
+  class Y {
+void x() { this->x(); x(); Y y; y.x(); a; this->b; Y::b; }
+template  void f() { this->f(); f(); }
+int a;
+static int b;
+  };
+  template 
+  class Z {
+void x() { this->m; }
+  };
+memberExpr(isArrow())
+  matches this->x, x, y.x, a, this->b
+cxxDependentScopeMemberExpr(isArrow())
+  matches this->m
+unresolvedMemberExpr(isArrow())
+  matches this->f, f
+
+
+
 MatcherCXXMethodDecl>isConst
 Matches if the given method declaration is const.
 
@@ -3228,11 +3254,20 @@
 Given
   class Y {
 void x() { this->x(); x(); Y y; y.x(); a; this->b; Y::b; }
+template  void f() { this->f(); f(); }
 int a;
 static int b;
   };
+  template 
+  class Z {
+void x() { this->m; }
+  };
 memberExpr(isArrow())
   matches this->x, x, y.x, a, this->b
+cxxDependentScopeMemberExpr(isArrow())
+  matches this->m
+unresolvedMemberExpr(isArrow())
+  matches this->f, f
 
 
 
@@ -3886,6 +3921,32 @@
 
 
 
+MatcherUnresolvedMemberExpr>isArrow
+Matches member expressions that are called with '->' as opposed
+to '.'.
+
+Member calls on the implicit this pointer match as called with '->'.
+
+Given
+  class Y {
+void x() { this->x(); x(); Y y; y.x(); a; this->b; Y::b; }
+template  void f() { this->f(); f(); }
+int a;
+static int b;
+  };
+  template 
+  class Z {
+void x() { this->m; }
+  };
+memberExpr(isArrow())
+  matches this->x, x, y.x, a, this->b
+cxxDependentScopeMemberExpr(isArrow())
+  matches this->m
+unresolvedMemberExpr(isArrow())
+  matches this->f, f
+
+
+
 MatcherVarDecl>hasAutomaticStorageDuration
 Matches a variable declaration that has automatic storage duration.
 
Index: cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
===
--- cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
+++ cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
@@ -4697,13 +4697,24 @@
 /// \code
 ///   class Y {
 /// void x() { this->x(); x(); Y y; y.x(); a; this->b; Y::b; }
+/// template  void f() { this->f(); f(); }
 /// int a;
 /// static int b;
 ///   };
+///   template 
+///   class Z {
+/// void x() { this->m; }
+///   };
 /// \endcode
 /// memberExpr(isArrow())
 ///   matches this->x, x, y.x, a, this->b
-AST_MATCHER(MemberExpr, isArrow) {
+/// cxxDependentScopeMemberExpr(isArrow())
+///   matches this->m
+/// unresolvedMemberExpr(isArrow())
+///   matches this->f, f
+AST_POLYMORPHIC_MATCHER(
+isArrow, AST_POLYMORPHIC_SUPPORTED_TYPES(MemberExpr, UnresolvedMemberExpr,
+ CXXDependentScopeMemberExpr)) {
   return Node.isArrow();
 }
 
Index: cfe/trunk/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
===
--- cfe/trunk/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ cfe/trunk/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -765,6 +765,11 @@
   memberExpr(isArrow(;
   EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } int y; };",
  memberExpr(isArrow(;
+  EXPECT_TRUE(matches("template  class Y { void x() { this->m; } };",
+  cxxDependentScopeMemberExpr(isArrow(;
+  EXPECT_TRUE(
+  notMatches("template  class Y { void x() { (*this).m; } };",
+ cxxDependentScopeMemberExpr(isArrow(;
 }
 
 TEST(IsArrow, MatchesStaticMemberVariablesViaArrow) {
@@ -783,6 +788,14 @@
   memberExpr(isArrow(;
   EXPECT_TRUE(notMatches("class Y { void x() { Y y; y.x(); } };",
  me

[PATCH] D52120: [analyzer] Treat std::{move, forward} as casts in ExprMutationAnalyzer.

2018-09-17 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 165801.
shuaiwang marked an inline comment as done.
shuaiwang added a comment.

Added test case with copy-ctor & assignment operator taking value as param.


Repository:
  rC Clang

https://reviews.llvm.org/D52120

Files:
  lib/Analysis/ExprMutationAnalyzer.cpp
  unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: unittests/Analysis/ExprMutationAnalyzerTest.cpp
===
--- unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -66,6 +66,25 @@
   return s;
 }
 
+const std::string StdRemoveReference =
+"namespace std {"
+"template struct remove_reference { typedef T type; };"
+"template struct remove_reference { typedef T type; };"
+"template struct remove_reference { typedef T type; }; }";
+
+const std::string StdMove =
+"namespace std {"
+"template typename remove_reference::type&& "
+"move(T&& t) noexcept {"
+"return static_cast::type&&>(t); } }";
+
+const std::string StdForward =
+"namespace std {"
+"template T&& "
+"forward(typename remove_reference::type& t) noexcept { return t; }"
+"template T&& "
+"forward(typename remove_reference::type&&) noexcept { return t; } }";
+
 } // namespace
 
 TEST(ExprMutationAnalyzerTest, Trivial) {
@@ -373,36 +392,87 @@
 }
 
 TEST(ExprMutationAnalyzerTest, Move) {
-  // Technically almost the same as ByNonConstRRefArgument, just double checking
-  const auto AST = tooling::buildASTFromCode(
-  "namespace std {"
-  "template struct remove_reference { typedef T type; };"
-  "template struct remove_reference { typedef T type; };"
-  "template struct remove_reference { typedef T type; };"
-  "template typename std::remove_reference::type&& "
-  "move(T&& t) noexcept; }"
-  "void f() { struct A {}; A x; std::move(x); }");
-  const auto Results =
+  auto AST =
+  tooling::buildASTFromCode(StdRemoveReference + StdMove +
+"void f() { struct A {}; A x; std::move(x); }");
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x)"));
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  StdRemoveReference + StdMove +
+  "void f() { struct A {}; A x, y; std::move(x) = y; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x) = y"));
+
+  AST = tooling::buildASTFromCode(StdRemoveReference + StdMove +
+  "void f() { int x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  StdRemoveReference + StdMove +
+  "struct S { S(); S(const S&); S& operator=(const S&); };"
+  "void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST =
+  tooling::buildASTFromCode(StdRemoveReference + StdMove +
+"struct S { S(); S(S&&); S& operator=(S&&); };"
+"void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)"));
+
+  AST =
+  tooling::buildASTFromCode(StdRemoveReference + StdMove +
+"struct S { S(); S(const S&); S(S&&);"
+"S& operator=(const S&); S& operator=(S&&); };"
+"void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)"));
+
+  AST = tooling::buildASTFromCode(
+  StdRemoveReference + StdMove +
+  "struct S { S(); S(const S&); S(S&&);"
+  "S& operator=(const S&); S& operator=(S&&); };"
+  "void f() { const S x; S y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(StdRemoveReference + StdMove +
+  "struct S { S(); S(S); S& operator=(S); };"
+  "void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  StdRemoveReference + StdMove +
+  "struct S{}; void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTCont

[PATCH] D52120: [analyzer] Treat std::{move, forward} as casts in ExprMutationAnalyzer.

2018-09-17 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rC342409: [analyzer] Treat std::{move,forward} as casts in 
ExprMutationAnalyzer. (authored by shuaiwang, committed by ).

Changed prior to commit:
  https://reviews.llvm.org/D52120?vs=165801&id=165813#toc

Repository:
  rC Clang

https://reviews.llvm.org/D52120

Files:
  lib/Analysis/ExprMutationAnalyzer.cpp
  unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: lib/Analysis/ExprMutationAnalyzer.cpp
===
--- lib/Analysis/ExprMutationAnalyzer.cpp
+++ lib/Analysis/ExprMutationAnalyzer.cpp
@@ -304,7 +304,16 @@
nonConstReferenceType()
 .bind(NodeID::value)),
 Stm, Context);
-  return findExprMutation(Casts);
+  if (const Stmt *S = findExprMutation(Casts))
+return S;
+  // Treat std::{move,forward} as cast.
+  const auto Calls =
+  match(findAll(callExpr(callee(namedDecl(
+ hasAnyName("::std::move", "::std::forward"))),
+ hasArgument(0, equalsNode(Exp)))
+.bind("expr")),
+Stm, Context);
+  return findExprMutation(Calls);
 }
 
 const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
@@ -360,7 +369,9 @@
   const auto IsInstantiated = hasDeclaration(isInstantiated());
   const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
   const auto Matches = match(
-  findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl),
+  findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
+  unless(callee(namedDecl(hasAnyName(
+  "::std::move", "::std::forward"),
  cxxConstructExpr(NonConstRefParam, IsInstantiated,
   FuncDecl)))
   .bind(NodeID::value)),
Index: unittests/Analysis/ExprMutationAnalyzerTest.cpp
===
--- unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -66,6 +66,25 @@
   return s;
 }
 
+const std::string StdRemoveReference =
+"namespace std {"
+"template struct remove_reference { typedef T type; };"
+"template struct remove_reference { typedef T type; };"
+"template struct remove_reference { typedef T type; }; }";
+
+const std::string StdMove =
+"namespace std {"
+"template typename remove_reference::type&& "
+"move(T&& t) noexcept {"
+"return static_cast::type&&>(t); } }";
+
+const std::string StdForward =
+"namespace std {"
+"template T&& "
+"forward(typename remove_reference::type& t) noexcept { return t; }"
+"template T&& "
+"forward(typename remove_reference::type&&) noexcept { return t; } }";
+
 } // namespace
 
 TEST(ExprMutationAnalyzerTest, Trivial) {
@@ -373,36 +392,87 @@
 }
 
 TEST(ExprMutationAnalyzerTest, Move) {
-  // Technically almost the same as ByNonConstRRefArgument, just double checking
-  const auto AST = tooling::buildASTFromCode(
-  "namespace std {"
-  "template struct remove_reference { typedef T type; };"
-  "template struct remove_reference { typedef T type; };"
-  "template struct remove_reference { typedef T type; };"
-  "template typename std::remove_reference::type&& "
-  "move(T&& t) noexcept; }"
-  "void f() { struct A {}; A x; std::move(x); }");
-  const auto Results =
+  auto AST =
+  tooling::buildASTFromCode(StdRemoveReference + StdMove +
+"void f() { struct A {}; A x; std::move(x); }");
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x)"));
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  StdRemoveReference + StdMove +
+  "void f() { struct A {}; A x, y; std::move(x) = y; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x) = y"));
+
+  AST = tooling::buildASTFromCode(StdRemoveReference + StdMove +
+  "void f() { int x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  StdRemoveReference + StdMove +
+  "struct S { S(); S(const S&); S& operator=(const S&); };"
+  "void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST =
+  tooling::buildASTFromCode(StdRemoveReference + StdMove +
+"struct S { 

[PATCH] D52120: [analyzer] Treat std::{move, forward} as casts in ExprMutationAnalyzer.

2018-09-17 Thread Shuai Wang via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rL342409: [analyzer] Treat std::{move,forward} as casts in 
ExprMutationAnalyzer. (authored by shuaiwang, committed by ).
Herald added a subscriber: llvm-commits.

Repository:
  rL LLVM

https://reviews.llvm.org/D52120

Files:
  cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp
  cfe/trunk/unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp
===
--- cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp
+++ cfe/trunk/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -304,7 +304,16 @@
nonConstReferenceType()
 .bind(NodeID::value)),
 Stm, Context);
-  return findExprMutation(Casts);
+  if (const Stmt *S = findExprMutation(Casts))
+return S;
+  // Treat std::{move,forward} as cast.
+  const auto Calls =
+  match(findAll(callExpr(callee(namedDecl(
+ hasAnyName("::std::move", "::std::forward"))),
+ hasArgument(0, equalsNode(Exp)))
+.bind("expr")),
+Stm, Context);
+  return findExprMutation(Calls);
 }
 
 const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
@@ -360,7 +369,9 @@
   const auto IsInstantiated = hasDeclaration(isInstantiated());
   const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
   const auto Matches = match(
-  findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl),
+  findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
+  unless(callee(namedDecl(hasAnyName(
+  "::std::move", "::std::forward"),
  cxxConstructExpr(NonConstRefParam, IsInstantiated,
   FuncDecl)))
   .bind(NodeID::value)),
Index: cfe/trunk/unittests/Analysis/ExprMutationAnalyzerTest.cpp
===
--- cfe/trunk/unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ cfe/trunk/unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -66,6 +66,25 @@
   return s;
 }
 
+const std::string StdRemoveReference =
+"namespace std {"
+"template struct remove_reference { typedef T type; };"
+"template struct remove_reference { typedef T type; };"
+"template struct remove_reference { typedef T type; }; }";
+
+const std::string StdMove =
+"namespace std {"
+"template typename remove_reference::type&& "
+"move(T&& t) noexcept {"
+"return static_cast::type&&>(t); } }";
+
+const std::string StdForward =
+"namespace std {"
+"template T&& "
+"forward(typename remove_reference::type& t) noexcept { return t; }"
+"template T&& "
+"forward(typename remove_reference::type&&) noexcept { return t; } }";
+
 } // namespace
 
 TEST(ExprMutationAnalyzerTest, Trivial) {
@@ -373,36 +392,87 @@
 }
 
 TEST(ExprMutationAnalyzerTest, Move) {
-  // Technically almost the same as ByNonConstRRefArgument, just double checking
-  const auto AST = tooling::buildASTFromCode(
-  "namespace std {"
-  "template struct remove_reference { typedef T type; };"
-  "template struct remove_reference { typedef T type; };"
-  "template struct remove_reference { typedef T type; };"
-  "template typename std::remove_reference::type&& "
-  "move(T&& t) noexcept; }"
-  "void f() { struct A {}; A x; std::move(x); }");
-  const auto Results =
+  auto AST =
+  tooling::buildASTFromCode(StdRemoveReference + StdMove +
+"void f() { struct A {}; A x; std::move(x); }");
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
-  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x)"));
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  StdRemoveReference + StdMove +
+  "void f() { struct A {}; A x, y; std::move(x) = y; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x) = y"));
+
+  AST = tooling::buildASTFromCode(StdRemoveReference + StdMove +
+  "void f() { int x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode(
+  StdRemoveReference + StdMove +
+  "struct S { S(); S(const S&); S& operator=(const S&); };"
+  "void f() { S x, y; y = std::move(x); }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST =
+  tooling::buildASTFromCode(StdRemoveReference + StdMove +
+

[PATCH] D52219: [analyzer] (1/n) Support pointee mutation analysis in ExprMutationAnalyzer.

2018-09-17 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang created this revision.
shuaiwang added reviewers: JonasToth, aaron.ballman.
Herald added subscribers: cfe-commits, Szelethus, mikhail.ramalho, a.sidorin, 
szepet, xazax.hun.
Herald added a reviewer: george.karpenkov.

We handle pointee mutation for native pointers & pointer-like types
(loosely defined as having an `operator*` returning non-const reference)

This diff alone just implemented the cases where pointee of an `Exp` is
*directly* mutated (e.g. invoking non-const member function.)

The most trivial class of casts is handled as well for easier unit
testing, `findPointeeCastMutation` is not done yet.

Planned future work:

- `findPointeeDerefMutation`: `Exp` is first dereferenced, and then mutated.
- `findPointee{Member,Array}Mutation`: member (or array element) of pointee is 
accessed and then mutated.
- `findPointeeArithmeticMutation`: handling pointer arithmetic
- `findPointeeAssignmentMutation`: pointer is assigned to another var and then 
mutated.


Repository:
  rC Clang

https://reviews.llvm.org/D52219

Files:
  include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
  lib/Analysis/ExprMutationAnalyzer.cpp
  unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: unittests/Analysis/ExprMutationAnalyzerTest.cpp
===
--- unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -40,16 +40,34 @@
 bool isMutated(const SmallVectorImpl &Results, ASTUnit *AST) {
   const auto *const S = selectFirst("stmt", Results);
   const auto *const E = selectFirst("expr", Results);
+  assert(E);
   return ExprMutationAnalyzer(*S, AST->getASTContext()).isMutated(E);
 }
 
+bool isPointeeMutated(const SmallVectorImpl &Results,
+  ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  const auto *const E = selectFirst("expr", Results);
+  assert(E);
+  return ExprMutationAnalyzer(*S, AST->getASTContext()).isPointeeMutated(E);
+}
+
 SmallVector
 mutatedBy(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const Stmt *(ExprMutationAnalyzer::*MutationFinder)(const Expr *) =
+  &ExprMutationAnalyzer::findMutation;
+  const Stmt *(ExprMutationAnalyzer::*PointeeMutationFinder)(const Expr *) =
+  &ExprMutationAnalyzer::findPointeeMutation;
   const auto *const S = selectFirst("stmt", Results);
   SmallVector Chain;
   ExprMutationAnalyzer Analyzer(*S, AST->getASTContext());
   for (const auto *E = selectFirst("expr", Results); E != nullptr;) {
-const Stmt *By = Analyzer.findMutation(E);
+auto Finder = MutationFinder;
+if (const auto *DRE = dyn_cast(E)) {
+  if (DRE->getNameInfo().getAsString()[0] == 'p')
+Finder = PointeeMutationFinder;
+}
+const Stmt *By = (Analyzer.*Finder)(E);
 std::string buffer;
 llvm::raw_string_ostream stream(buffer);
 By->printPretty(stream, nullptr, AST->getASTContext().getPrintingPolicy());
@@ -88,10 +106,14 @@
 } // namespace
 
 TEST(ExprMutationAnalyzerTest, Trivial) {
-  const auto AST = tooling::buildASTFromCode("void f() { int x; x; }");
-  const auto Results =
+  auto AST = tooling::buildASTFromCode("void f() { int x; x; }");
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = tooling::buildASTFromCode("void f() { const int x = 0; x; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
 class AssignmentTest : public ::testing::TestWithParam {};
@@ -124,42 +146,104 @@
 Values("++x", "--x", "x++", "x--"), );
 
 TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) {
-  const auto AST = tooling::buildASTFromCode(
+  auto AST = tooling::buildASTFromCode(
   "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }");
-  const auto Results =
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_TRUE(isMutated(Results, AST.get()));
+  EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+
+  AST = tooling::buildASTFromCode(
+  "void f() { struct Foo { void mf(); }; Foo *p; p->mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+  EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("p->mf()"));
 }
 
 TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) {
   auto AST = tooling::buildASTFromCodeWithArgs(
   "struct X { template  void mf(); };"
-  "template  void f() { X x; x.mf(); }",
+  "template  void f() { X x; x.mf(); }"
+  "template  void g() { X *p; p->mf(); }",
   {"-fno-delayed-template-parsing"});
   auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXP

[PATCH] D52219: [analyzer] (1/n) Support pointee mutation analysis in ExprMutationAnalyzer.

2018-09-18 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang updated this revision to Diff 166065.
shuaiwang added a comment.

Rebase


Repository:
  rC Clang

https://reviews.llvm.org/D52219

Files:
  include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
  lib/Analysis/ExprMutationAnalyzer.cpp
  unittests/Analysis/ExprMutationAnalyzerTest.cpp

Index: unittests/Analysis/ExprMutationAnalyzerTest.cpp
===
--- unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -52,16 +52,34 @@
 bool isMutated(const SmallVectorImpl &Results, ASTUnit *AST) {
   const auto *const S = selectFirst("stmt", Results);
   const auto *const E = selectFirst("expr", Results);
+  assert(E);
   return ExprMutationAnalyzer(*S, AST->getASTContext()).isMutated(E);
 }
 
+bool isPointeeMutated(const SmallVectorImpl &Results,
+  ASTUnit *AST) {
+  const auto *const S = selectFirst("stmt", Results);
+  const auto *const E = selectFirst("expr", Results);
+  assert(E);
+  return ExprMutationAnalyzer(*S, AST->getASTContext()).isPointeeMutated(E);
+}
+
 SmallVector
 mutatedBy(const SmallVectorImpl &Results, ASTUnit *AST) {
+  const Stmt *(ExprMutationAnalyzer::*MutationFinder)(const Expr *) =
+  &ExprMutationAnalyzer::findMutation;
+  const Stmt *(ExprMutationAnalyzer::*PointeeMutationFinder)(const Expr *) =
+  &ExprMutationAnalyzer::findPointeeMutation;
   const auto *const S = selectFirst("stmt", Results);
   SmallVector Chain;
   ExprMutationAnalyzer Analyzer(*S, AST->getASTContext());
   for (const auto *E = selectFirst("expr", Results); E != nullptr;) {
-const Stmt *By = Analyzer.findMutation(E);
+auto Finder = MutationFinder;
+if (const auto *DRE = dyn_cast(E)) {
+  if (DRE->getNameInfo().getAsString()[0] == 'p')
+Finder = PointeeMutationFinder;
+}
+const Stmt *By = (Analyzer.*Finder)(E);
 std::string buffer;
 llvm::raw_string_ostream stream(buffer);
 By->printPretty(stream, nullptr, AST->getASTContext().getPrintingPolicy());
@@ -100,10 +118,14 @@
 } // namespace
 
 TEST(ExprMutationAnalyzerTest, Trivial) {
-  const auto AST = buildASTFromCode("void f() { int x; x; }");
-  const auto Results =
+  auto AST = buildASTFromCode("void f() { int x; x; }");
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_FALSE(isMutated(Results, AST.get()));
+
+  AST = buildASTFromCode("void f() { const int x = 0; x; }");
+  Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
 }
 
 class AssignmentTest : public ::testing::TestWithParam {};
@@ -134,41 +156,104 @@
 Values("++x", "--x", "x++", "x--"), );
 
 TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) {
-  const auto AST = buildASTFromCode(
+  auto AST = buildASTFromCode(
   "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }");
-  const auto Results =
+  auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+  EXPECT_TRUE(isMutated(Results, AST.get()));
+  EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+
+  AST = buildASTFromCode(
+  "void f() { struct Foo { void mf(); }; Foo *p; p->mf(); }");
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_FALSE(isMutated(Results, AST.get()));
+  EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("p->mf()"));
 }
 
 TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) {
   auto AST = buildASTFromCodeWithArgs(
   "struct X { template  void mf(); };"
-  "template  void f() { X x; x.mf(); }",
+  "template  void f() { X x; x.mf(); }"
+  "template  void g() { X *p; p->mf(); }",
   {"-fno-delayed-template-parsing"});
   auto Results =
   match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("p->mf()"));
 
-  AST = buildASTFromCodeWithArgs("template  void f() { T x; x.mf(); }",
- {"-fno-delayed-template-parsing"});
+  AST =
+  buildASTFromCodeWithArgs("template  void f() { T x; x.mf(); }"
+   "template  void g() { T *p; p->mf(); }",
+   {"-fno-delayed-template-parsing"});
   Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
   EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()"));
+  Results = match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+  EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("p->mf()"));
 
   AST = buildASTFromCodeWithArgs(
   "template  struct X;"
-  "temp

[PATCH] D52219: [analyzer] (1/n) Support pointee mutation analysis in ExprMutationAnalyzer.

2018-09-18 Thread Shuai Wang via Phabricator via cfe-commits
shuaiwang added a comment.

In https://reviews.llvm.org/D52219#1238423, @JonasToth wrote:

> Do you think it would be possible to the analysis for `>const?< int 
> ***`-cases? (recursively checking through the pointer levels)


I think that should be possible, will do after single-layer pointee analysis is 
done. I believe we can build multi-layer analysis based on much of the 
single-layer analysis.




Comment at: lib/Analysis/ExprMutationAnalyzer.cpp:198
 const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
+  // `Exp` can be *directly* mutated if the type of `Exp` is not const.
+  // Bail out early otherwise.

JonasToth wrote:
> Just to be sure that i understand:
> the changes here are more performance optimizations then directly related to 
> detect pointee mutations?
Yes :)



Comment at: lib/Analysis/ExprMutationAnalyzer.cpp:481
+  const auto AsArg =
+  anyOf(callExpr(hasAnyArgument(equalsNode(Exp))),
+cxxConstructExpr(hasAnyArgument(equalsNode(Exp))),

JonasToth wrote:
> shouldn't be the constness of the argument considered here?
We need that for non-pointee version, but not for pointee version, for example:
```
void g1(int * const);

void f1() {
  int *x;
  g1(x); // <-- x is passed to `g1`, we consider that as a mutation, the 
argument type do have a top-level const
}

void g2(const int *);

void f2() {
  int *x;
  g2(x); // <-- declRefExp(to(x)) is NOT directly passed to `g2`, there's a 
layer a ImplicitCastExpr in between, and after the implicit cast, the 
type of the expression becomes "const int *" instead of just "int*", so it'll 
fail the `isPointeeMutable` check at the beginning of 
`findPointeeDirectMutation`
}
```

In summary, we rely on:
- Checking whether pointee is actually mutable at the beginning
- Carefully handling casts by not trivially ignoring them unless absolutely safe


Repository:
  rC Clang

https://reviews.llvm.org/D52219



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


  1   2   >