ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: NoQ, jkorous, t-rasmud, malavikasamak.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

We have to give up on fixing a variable declaration if it has specifiers that 
are not supported yet.  We cannot support them for now due to the combination 
of following challenges:

- Sometimes we do not have accurate source range information for the type 
specifiers of a variable declaration,  especially when cv-qualifiers are used 
(source location is not provided for type qualifiers 
<https://github.com/llvm/llvm-project/blob/5fa5c39871c8ea7df2173951be3a4a17b29fa42e/clang/include/clang/AST/TypeLoc.h#L280C15-L280C15>).
   So it is hard to generate fix-its that only touch type specifiers for a 
declaration.
- We do not have the source range information for most declaration specifiers, 
such as storage specifiers.  So it is hard to tell whether a fix-it may quietly 
remove a specifier by accidentally overwriting it.
- When the declarator is not a trivial identifier (e.g., `int a[]` as a 
parameter decl),  we have to replace the whole declaration in order to fix it 
(e.g., to `std::span<int> a`).  If there are other specifiers involved, they 
may be accidentally removed (e.g.,  fix `int [[some_type_attribute]] a[]` to 
`std::span<int> a` is incorrect).

We could support these specifiers incrementally using the same approach as how 
we deal with cv-qualifiers.  If a fixing variable declaration has a storage 
specifier, instead of trying to find out the source location of the specifier 
or to avoid touching it, we add the keyword to a canonicalized place in the 
fix-it text that replaces the whole declaration.

For example, storage specifiers could be always placed at the beginning of a 
declaration. So both `const static int * x` and `static const int * x` will be 
fixed to `static std::span<const int> x`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D156192

Files:
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
===================================================================
--- clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-local-var-span.cpp
@@ -56,12 +56,46 @@
   // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:24}:"std::span<int const> const q"
   // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:25-[[@LINE-2]]:25}:"{"
   // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:26-[[@LINE-3]]:26}:", 10}"
+  [[deprecated]] const int * x = a;
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:18-[[@LINE-1]]:33}:"std::span<int const> x"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:34-[[@LINE-2]]:34}:"{"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:35-[[@LINE-3]]:35}:", 10}"
+  const int * y [[deprecated]];
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:16}:"std::span<int const> y"
+
   int tmp;
+
   tmp = p[5];
   tmp = q[5];
+  tmp = x[5];
+  tmp = y[5];
 }
 
 
+void local_variable_unsupported_specifiers() {
+  int a[10];
+  const int * p [[deprecated]] = a; //  not supported because the attribute overlaps the source range of the declaration
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+
+  static const int * q = a; //  storage specifier not supported yet
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+
+  extern int * x; //  storage specifier not supported yet
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+
+  constexpr int * y = 0; //  `constexpr` specifier not supported yet
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
+
+  int tmp;
+
+  tmp = p[5];
+  tmp = q[5];
+  tmp = x[5];
+  tmp = y[5];
+}
+
+
+
 void local_array_subscript_variable_extent() {
   int n = 10;
   int tmp;
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===================================================================
--- clang/lib/Analysis/UnsafeBufferUsage.cpp
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -1381,6 +1381,26 @@
   return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);
 }
 
+// We cannot fix a variable declaration if it has some other specifiers than the
+// type specifier.  Because the source ranges of those specifiers could overlap
+// with the source range that is being replaced using fix-its.  Especially when
+// we often cannot obtain accruate source ranges of cv-qualified type
+// specifiers.
+static bool hasUnsupportedSpecifiers(const VarDecl *VD,
+                                     const SourceManager &SM) {
+  // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
+  // source range of `VD`:
+  bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
+    return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
+                                          VD->getBeginLoc())) &&
+           !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
+                                          At->getRange().getBegin()));
+  });
+  return VD->isInlineSpecified() || VD->isConstexpr() ||
+         VD->hasConstantInitialization() || !VD->hasLocalStorage() ||
+         AttrRangeOverlapping;
+}
+
 // Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
 // type. The text is obtained through from `TypeLoc`s.  Since `TypeLoc` does not
 // have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me
@@ -1801,6 +1821,9 @@
 //    list otherwise.
 static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
                                          const StringRef UserFillPlaceHolder) {
+  if (hasUnsupportedSpecifiers(D, Ctx.getSourceManager()))
+    return {};
+
   FixItList FixIts{};
   std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx);
 
@@ -2028,6 +2051,8 @@
 // `createOverloadsForFixedParams`).
 static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
                                   UnsafeBufferUsageHandler &Handler) {
+  if (hasUnsupportedSpecifiers(PVD, Ctx.getSourceManager()))
+    return {};
   if (PVD->hasDefaultArg())
     // FIXME: generate fix-its for default values:
     return {};
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to