This revision was automatically updated to reflect the committed changes.
Closed by commit rL368147: gsl::Owner/gsl::Pointer: Add implicit annotations 
for some std types (authored by mgehre, committed by ).
Herald added a project: LLVM.
Herald added a subscriber: llvm-commits.

Changed prior to commit:
  https://reviews.llvm.org/D64448?vs=212446&id=213836#toc

Repository:
  rL LLVM

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

https://reviews.llvm.org/D64448

Files:
  cfe/trunk/include/clang/Basic/AttrDocs.td
  cfe/trunk/include/clang/Sema/Sema.h
  cfe/trunk/lib/Sema/SemaAttr.cpp
  cfe/trunk/lib/Sema/SemaDecl.cpp
  cfe/trunk/lib/Sema/SemaTemplate.cpp
  cfe/trunk/test/SemaCXX/attr-gsl-owner-pointer-std.cpp

Index: cfe/trunk/include/clang/Sema/Sema.h
===================================================================
--- cfe/trunk/include/clang/Sema/Sema.h
+++ cfe/trunk/include/clang/Sema/Sema.h
@@ -6116,6 +6116,17 @@
       ClassTemplateSpecializationDecl *BaseTemplateSpec,
       SourceLocation BaseLoc);
 
+  /// Add gsl::Pointer attribute to std::container::iterator
+  /// \param ND The declaration that introduces the name
+  /// std::container::iterator. \param UnderlyingRecord The record named by ND.
+  void inferGslPointerAttribute(NamedDecl *ND, CXXRecordDecl *UnderlyingRecord);
+
+  /// Add [[gsl::Owner]] and [[gsl::Pointer]] attributes for std:: types.
+  void inferGslOwnerPointerAttribute(CXXRecordDecl *Record);
+
+  /// Add [[gsl::Pointer]] attributes for std:: types.
+  void inferGslPointerAttribute(TypedefNameDecl *TD);
+
   void CheckCompletedCXXClass(CXXRecordDecl *Record);
 
   /// Check that the C++ class annoated with "trivial_abi" satisfies all the
Index: cfe/trunk/include/clang/Basic/AttrDocs.td
===================================================================
--- cfe/trunk/include/clang/Basic/AttrDocs.td
+++ cfe/trunk/include/clang/Basic/AttrDocs.td
@@ -4249,7 +4249,7 @@
     int *getInt() { return &value; }
   };
 
-The argument ``T`` is optional and currently ignored.
+The argument ``T`` is optional and is ignored.
 This attribute may be used by analysis tools and has no effect on code
 generation.
 
@@ -4275,7 +4275,7 @@
     int *getInt() { return &valuePointer; }
   };
 
-The argument ``T`` is optional and currently ignored.
+The argument ``T`` is optional and is ignored.
 This attribute may be used by analysis tools and has no effect on code
 generation.
 
Index: cfe/trunk/test/SemaCXX/attr-gsl-owner-pointer-std.cpp
===================================================================
--- cfe/trunk/test/SemaCXX/attr-gsl-owner-pointer-std.cpp
+++ cfe/trunk/test/SemaCXX/attr-gsl-owner-pointer-std.cpp
@@ -0,0 +1,129 @@
+// RUN: %clang_cc1 -ast-dump %s | \
+// RUN: FileCheck --implicit-check-not OwnerAttr --implicit-check-not PointerAttr %s
+
+// Test attribute inference for types in the standard library.
+namespace std {
+// Attributes are inferred for a (complete) class.
+class any {
+  // CHECK: CXXRecordDecl {{.*}} any
+  // CHECK: OwnerAttr {{.*}}
+};
+
+// Attributes are inferred for instantiations of a complete template.
+template <typename T>
+class vector {
+public:
+  class iterator {};
+  // CHECK: ClassTemplateDecl {{.*}} vector
+  // CHECK: OwnerAttr {{.*}}
+  // CHECK: CXXRecordDecl {{.*}} iterator
+  // CHECK: PointerAttr {{.*}}
+  // CHECK: ClassTemplateSpecializationDecl {{.*}} vector
+  // CHECK: TemplateArgument type 'int'
+  // CHECK: OwnerAttr
+  // CHECK: CXXRecordDecl {{.*}} iterator
+  // CHECK: PointerAttr {{.*}}
+};
+static_assert(sizeof(vector<int>), "");           // Force instantiation.
+static_assert(sizeof(vector<int>::iterator), ""); // Force instantiation.
+
+// If std::container::iterator is a using declaration, attributes are inferred
+// for the underlying class.
+template <typename T>
+class __set_iterator {};
+// CHECK: ClassTemplateDecl {{.*}} __set_iterator
+// CHECK: PointerAttr
+// CHECK: ClassTemplateSpecializationDecl {{.*}} __set_iterator
+// CHECK: TemplateArgument type 'int'
+// CHECK: PointerAttr
+
+template <typename T>
+class set {
+  // CHECK: ClassTemplateDecl {{.*}} set
+  // CHECK: OwnerAttr {{.*}}
+  // CHECK: ClassTemplateSpecializationDecl {{.*}} set
+  // CHECK: OwnerAttr {{.*}}
+public:
+  using iterator = __set_iterator<T>;
+};
+static_assert(sizeof(set<int>::iterator), ""); // Force instantiation.
+
+// If std::container::iterator is a typedef, attributes are inferred for the
+// underlying class.
+template <typename T>
+class __map_iterator {};
+// CHECK: ClassTemplateDecl {{.*}} __map_iterator
+// CHECK: PointerAttr
+// CHECK: ClassTemplateSpecializationDecl {{.*}} __map_iterator
+// CHECK: TemplateArgument type 'int'
+// CHECK: PointerAttr
+
+template <typename T>
+class map {
+  // CHECK: ClassTemplateDecl {{.*}} map
+  // CHECK: OwnerAttr {{.*}}
+  // CHECK: ClassTemplateSpecializationDecl {{.*}} map
+  // CHECK: OwnerAttr {{.*}}
+public:
+  typedef __map_iterator<T> iterator;
+};
+static_assert(sizeof(map<int>::iterator), ""); // Force instantiation.
+
+// Inline namespaces are ignored when checking if
+// the class lives in the std namespace.
+inline namespace inlinens {
+template <typename T>
+class __unordered_map_iterator {};
+// CHECK: ClassTemplateDecl {{.*}} __unordered_map_iterator
+// CHECK: PointerAttr
+// CHECK: ClassTemplateSpecializationDecl {{.*}} __unordered_map_iterator
+// CHECK: TemplateArgument type 'int'
+// CHECK: PointerAttr
+
+template <typename T>
+class unordered_map {
+  // CHECK: ClassTemplateDecl {{.*}} unordered_map
+  // CHECK: OwnerAttr {{.*}}
+  // CHECK: ClassTemplateSpecializationDecl {{.*}} unordered_map
+  // CHECK: OwnerAttr {{.*}}
+public:
+  typedef __unordered_map_iterator<T> iterator;
+};
+static_assert(sizeof(unordered_map<int>::iterator), ""); // Force instantiation.
+} // namespace inlinens
+
+// std::list has an implicit gsl::Owner attribute,
+// but explicit attributes take precedence.
+template <typename T>
+class [[gsl::Pointer]] list{};
+// CHECK: ClassTemplateDecl {{.*}} list
+// CHECK: PointerAttr {{.*}}
+// CHECK: ClassTemplateSpecializationDecl {{.*}} list
+// CHECK: PointerAttr {{.*}}
+
+static_assert(sizeof(list<int>), ""); // Force instantiation.
+
+// Forward declared template (Owner).
+template <
+    class CharT,
+    class Traits>
+class basic_regex;
+// CHECK: ClassTemplateDecl {{.*}} basic_regex
+// CHECK: OwnerAttr {{.*}}
+
+// Forward declared template (Pointer).
+template <class T>
+class reference_wrapper;
+// CHECK: ClassTemplateDecl {{.*}} reference_wrapper
+// CHECK: PointerAttr {{.*}}
+
+class some_unknown_type;
+// CHECK: CXXRecordDecl {{.*}} some_unknown_type
+
+} // namespace std
+
+namespace user {
+// If a class is not in the std namespace, we don't infer the attributes.
+class any {
+};
+} // namespace user
Index: cfe/trunk/lib/Sema/SemaAttr.cpp
===================================================================
--- cfe/trunk/lib/Sema/SemaAttr.cpp
+++ cfe/trunk/lib/Sema/SemaAttr.cpp
@@ -85,6 +85,126 @@
         MSVtorDispAttr::CreateImplicit(Context, VtorDispStack.CurrentValue));
 }
 
+template <typename Attribute>
+static void addGslOwnerPointerAttributeIfNotExisting(ASTContext &Context,
+                                                     CXXRecordDecl *Record) {
+  CXXRecordDecl *Canonical = Record->getCanonicalDecl();
+  if (Canonical->hasAttr<OwnerAttr>() || Canonical->hasAttr<PointerAttr>())
+    return;
+
+  Canonical->addAttr(::new (Context) Attribute(SourceRange{}, Context,
+                                               /*DerefType*/ nullptr,
+                                               /*Spelling=*/0));
+}
+
+void Sema::inferGslPointerAttribute(NamedDecl *ND,
+                                    CXXRecordDecl *UnderlyingRecord) {
+  if (!UnderlyingRecord)
+    return;
+
+  const auto *Parent = dyn_cast<CXXRecordDecl>(ND->getDeclContext());
+  if (!Parent)
+    return;
+
+  static llvm::StringSet<> Containers{
+      "array",
+      "basic_string",
+      "deque",
+      "forward_list",
+      "vector",
+      "list",
+      "map",
+      "multiset",
+      "multimap",
+      "priority_queue",
+      "queue",
+      "set",
+      "stack",
+      "unordered_set",
+      "unordered_map",
+      "unordered_multiset",
+      "unordered_multimap",
+  };
+
+  static llvm::StringSet<> Iterators{"iterator", "const_iterator",
+                                     "reverse_iterator",
+                                     "const_reverse_iterator"};
+
+  if (Parent->isInStdNamespace() && Iterators.count(ND->getName()) &&
+      Containers.count(Parent->getName()))
+    addGslOwnerPointerAttributeIfNotExisting<PointerAttr>(Context,
+                                                          UnderlyingRecord);
+}
+
+void Sema::inferGslPointerAttribute(TypedefNameDecl *TD) {
+
+  QualType Canonical = TD->getUnderlyingType().getCanonicalType();
+
+  CXXRecordDecl *RD = Canonical->getAsCXXRecordDecl();
+  if (!RD) {
+    if (auto *TST =
+            dyn_cast<TemplateSpecializationType>(Canonical.getTypePtr())) {
+
+      RD = dyn_cast_or_null<CXXRecordDecl>(
+          TST->getTemplateName().getAsTemplateDecl()->getTemplatedDecl());
+    }
+  }
+
+  inferGslPointerAttribute(TD, RD);
+}
+
+void Sema::inferGslOwnerPointerAttribute(CXXRecordDecl *Record) {
+  static llvm::StringSet<> StdOwners{
+      "any",
+      "array",
+      "basic_regex",
+      "basic_string",
+      "deque",
+      "forward_list",
+      "vector",
+      "list",
+      "map",
+      "multiset",
+      "multimap",
+      "optional",
+      "priority_queue",
+      "queue",
+      "set",
+      "stack",
+      "unique_ptr",
+      "unordered_set",
+      "unordered_map",
+      "unordered_multiset",
+      "unordered_multimap",
+      "variant",
+  };
+  static llvm::StringSet<> StdPointers{
+      "basic_string_view",
+      "reference_wrapper",
+      "regex_iterator",
+  };
+
+  if (!Record->getIdentifier())
+    return;
+
+  // Handle classes that directly appear in std namespace.
+  if (Record->isInStdNamespace()) {
+    CXXRecordDecl *Canonical = Record->getCanonicalDecl();
+    if (Canonical->hasAttr<OwnerAttr>() || Canonical->hasAttr<PointerAttr>())
+      return;
+
+    if (StdOwners.count(Record->getName()))
+      addGslOwnerPointerAttributeIfNotExisting<OwnerAttr>(Context, Record);
+    else if (StdPointers.count(Record->getName()))
+      addGslOwnerPointerAttributeIfNotExisting<PointerAttr>(Context, Record);
+
+    return;
+  }
+
+  // Handle nested classes that could be a gsl::Pointer.
+  inferGslPointerAttribute(Record, Record);
+}
+
 void Sema::ActOnPragmaOptionsAlign(PragmaOptionsAlignKind Kind,
                                    SourceLocation PragmaLoc) {
   PragmaMsStackAction Action = Sema::PSK_Reset;
Index: cfe/trunk/lib/Sema/SemaTemplate.cpp
===================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp
@@ -1690,6 +1690,7 @@
     mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl());
 
   AddPushedVisibilityAttribute(NewClass);
+  inferGslOwnerPointerAttribute(NewClass);
 
   if (TUK != TUK_Friend) {
     // Per C++ [basic.scope.temp]p2, skip the template parameter scopes.
Index: cfe/trunk/lib/Sema/SemaDecl.cpp
===================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp
+++ cfe/trunk/lib/Sema/SemaDecl.cpp
@@ -5858,6 +5858,8 @@
   if (!Previous.empty()) {
     Redeclaration = true;
     MergeTypedefNameDecl(S, NewTD, Previous);
+  } else {
+    inferGslPointerAttribute(NewTD);
   }
 
   if (ShadowedDecl && !Redeclaration)
@@ -15131,6 +15133,9 @@
   if (PrevDecl)
     mergeDeclAttributes(New, PrevDecl);
 
+  if (auto *CXXRD = dyn_cast<CXXRecordDecl>(New))
+    inferGslOwnerPointerAttribute(CXXRD);
+
   // If there's a #pragma GCC visibility in scope, set the visibility of this
   // record.
   AddPushedVisibilityAttribute(New);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to