cor3ntin updated this revision to Diff 411101.
cor3ntin added a comment.

Fix comment to address Christopher's feedback


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D119670

Files:
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaExpr.cpp
  clang/test/SemaCXX/unqualified-std-call-fixits.cpp
  clang/test/SemaCXX/unqualified-std-call.cpp
  clang/test/SemaCXX/warn-self-move.cpp

Index: clang/test/SemaCXX/warn-self-move.cpp
===================================================================
--- clang/test/SemaCXX/warn-self-move.cpp
+++ clang/test/SemaCXX/warn-self-move.cpp
@@ -17,7 +17,8 @@
   (x) = std::move(x);  // expected-warning{{explicitly moving}}
 
   using std::move;
-  x = move(x);  // expected-warning{{explicitly moving}}
+  x = move(x); // expected-warning{{explicitly moving}} \
+                   expected-warning {{unqualified call to std::move}}
 }
 
 int global;
@@ -26,7 +27,8 @@
   (global) = std::move(global);  // expected-warning{{explicitly moving}}
 
   using std::move;
-  global = move(global);  // expected-warning{{explicitly moving}}
+  global = move(global); // expected-warning{{explicitly moving}} \
+                             expected-warning {{unqualified call to std::move}}
 }
 
 class field_test {
Index: clang/test/SemaCXX/unqualified-std-call.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/unqualified-std-call.cpp
@@ -0,0 +1,118 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wall -std=c++11 %s
+
+namespace std {
+
+template <typename T>
+void dummy(T &&) {}
+template <typename T>
+void move(T &&) {}
+template <typename T, typename U>
+void move(T &&, U &&) {}
+
+inline namespace __1 {
+template <typename T>
+void forward(T &) {}
+} // namespace __1
+
+struct foo {};
+
+} // namespace std
+
+namespace global {
+
+using namespace std;
+
+void f() {
+  int i = 0;
+  std::move(i);
+  move(i);   // expected-warning{{unqualified call to std::move}}
+  (move)(i); // expected-warning{{unqualified call to std::move}}
+  std::dummy(1);
+  dummy(1);
+  std::move(1, 2);
+  move(1, 2);
+  forward<int>(i); // expected-warning{{unqualified call to std::forward}}
+  std::forward<int>(i);
+}
+
+template <typename T>
+void g(T &&foo) {
+  std::move(foo);
+  move(foo); // expected-warning{{unqualified call to std::move}}
+
+  std::forward<decltype(foo)>(foo);
+  forward<decltype(foo)>(foo); // expected-warning{{unqualified call to std::forward}}
+  move(1, 2);
+  dummy(foo);
+}
+
+void call() {
+  g(0); //expected-note {{here}}
+}
+
+} // namespace global
+
+namespace named {
+
+using std::forward;
+using std::move;
+
+void f() {
+  int i = 0;
+  move(i); // expected-warning{{unqualified call to std::move}}
+  move(1, 2);
+  forward<int>(i); // expected-warning{{unqualified call to std::forward}}
+}
+
+template <typename T>
+void g(T &&foo) {
+  move(foo);                     // expected-warning{{unqualified call to std::move}}
+  forward<decltype(foo)>(foo);   // expected-warning{{unqualified call to std::forward}}
+  (forward<decltype(foo)>)(foo); // expected-warning{{unqualified call to std::forward}}
+  move(1, 2);
+}
+
+void call() {
+  g(0); //expected-note {{here}}
+}
+
+} // namespace named
+
+namespace overload {
+using namespace std;
+template <typename T>
+int move(T &&);
+void f() {
+  int i = 0;
+  move(i);
+}
+} // namespace overload
+
+namespace adl {
+void f() {
+  move(std::foo{}); // expected-warning{{unqualified call to std::move}}
+}
+
+} // namespace adl
+
+namespace std {
+
+void f() {
+  int i = 0;
+  move(i);         // expected-warning{{unqualified call to std::move}}
+  forward<int>(i); // expected-warning{{unqualified call to std::forward}}
+}
+
+} // namespace std
+
+namespace test_alias {
+namespace alias = std;
+using namespace alias;
+void f() {
+  int i = 0;
+  move(i); // expected-warning{{unqualified call to std::move}}
+  move(1, 2);
+  forward<int>(i); // expected-warning{{unqualified call to std::forward}}
+}
+
+} // namespace test_alias
Index: clang/test/SemaCXX/unqualified-std-call-fixits.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/unqualified-std-call-fixits.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -verify -std=c++20 -Wall %s
+// RUN: cp %s %t
+// RUN: %clang_cc1 -x c++ -std=c++20 -fixit %t
+// RUN: %clang_cc1 -Wall -Werror -x c++ -std=c++20 %t
+// RUN: cat %t | FileCheck %s
+
+namespace std {
+
+void move(auto &&a) {}
+
+void forward(auto &a) {}
+
+} // namespace std
+
+using namespace std;
+
+void f() {
+  int i = 0;
+  move(i); // expected-warning {{unqualified call to std::move}}
+  // CHECK: {{^}}  std::
+  forward(i); // expected-warning {{unqualified call to std::forward}}
+              // CHECK: {{^}}  std::
+}
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -6416,6 +6416,38 @@
   }
 }
 
+// Once a call is fully resolved, warn for unqualified calls to specific
+// C++ standard functions, like move and forward
+static void DiagnosedUnqualifiedCallsToStdFunctions(Sema &S, CallExpr *Call) {
+  // We are only checking unary move and forward so exit early here
+  if (Call->getNumArgs() != 1)
+    return;
+
+  Expr *E = Call->getCallee()->IgnoreParenImpCasts();
+  if (!E || isa<UnresolvedLookupExpr>(E))
+    return;
+  DeclRefExpr *DRE = dyn_cast_or_null<DeclRefExpr>(E);
+  if (!DRE || !DRE->getLocation().isValid())
+    return;
+
+  if (DRE->getQualifier())
+    return;
+
+  NamedDecl *D = dyn_cast_or_null<NamedDecl>(Call->getCalleeDecl());
+  if (!D || !D->isInStdNamespace())
+    return;
+
+  // Only warn for some functions deemed more frequent or problematic
+  static constexpr llvm::StringRef SpecialFunctions[] = {"move", "forward"};
+  auto it = llvm::find(SpecialFunctions, D->getName());
+  if (it == std::end(SpecialFunctions))
+    return;
+
+  S.Diag(DRE->getLocation(), diag::warn_unqualified_call_to_std_cast_function)
+      << D->getQualifiedNameAsString()
+      << FixItHint::CreateInsertion(DRE->getLocation(), "std::");
+}
+
 ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc,
                                MultiExprArg ArgExprs, SourceLocation RParenLoc,
                                Expr *ExecConfig) {
@@ -6440,7 +6472,11 @@
   if (LangOpts.OpenMP)
     Call = ActOnOpenMPCall(Call, Scope, LParenLoc, ArgExprs, RParenLoc,
                            ExecConfig);
-
+  if (LangOpts.CPlusPlus) {
+    CallExpr *CE = dyn_cast<CallExpr>(Call.get());
+    if (CE)
+      DiagnosedUnqualifiedCallsToStdFunctions(*this, CE);
+  }
   return Call;
 }
 
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4808,6 +4808,9 @@
   "use of function template name with no prior declaration in function call "
   "with explicit template arguments is a C++20 extension">, InGroup<CXX20>;
 
+def warn_unqualified_call_to_std_cast_function : Warning<
+  "unqualified call to %0">, InGroup<DiagGroup<"unqualified-std-cast-call">>;
+
 // C++ Template Argument Lists
 def err_template_missing_args : Error<
   "use of "
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to