https://github.com/ykhatav created 
https://github.com/llvm/llvm-project/pull/225081

…rguments

>From e17db0202d9d0ad4602ff9ab39a7094b1751e949 Mon Sep 17 00:00:00 2001
From: "Khatavkar, Yashasvi" <[email protected]>
Date: Mon, 21 Sep 2026 06:17:20 -0700
Subject: [PATCH] Add support for adjust_args extensions in OMP6.0 to take
 positional arguments

---
 clang/include/clang-c/Index.h                 |  14 +-
 clang/include/clang/AST/ComputeDependence.h   |   4 +
 clang/include/clang/AST/ExprOpenMP.h          | 147 +++++++++++
 clang/include/clang/AST/OpenMPClause.h        |  23 ++
 clang/include/clang/AST/RecursiveASTVisitor.h |   2 +
 clang/include/clang/AST/TextNodeDumper.h      |   1 +
 .../clang/Basic/DiagnosticParseKinds.td       |   4 +
 .../clang/Basic/DiagnosticSemaKinds.td        |   3 +
 clang/include/clang/Basic/StmtNodes.td        |   2 +
 clang/include/clang/Parse/Parser.h            |  11 +
 clang/include/clang/Sema/SemaOpenMP.h         |  13 +
 .../include/clang/Serialization/ASTBitCodes.h |   2 +
 clang/lib/AST/AttrImpl.cpp                    |  19 +-
 clang/lib/AST/ComputeDependence.cpp           |  20 ++
 clang/lib/AST/Expr.cpp                        |   2 +
 clang/lib/AST/ExprClassification.cpp          |   2 +
 clang/lib/AST/ExprConstant.cpp                |   2 +
 clang/lib/AST/ItaniumMangle.cpp               |   2 +
 clang/lib/AST/OpenMPClause.cpp                |  88 +++++++
 clang/lib/AST/StmtPrinter.cpp                 |  20 ++
 clang/lib/AST/StmtProfile.cpp                 |  13 +
 clang/lib/AST/TextNodeDumper.cpp              |   6 +
 clang/lib/Parse/ParseOpenMP.cpp               | 114 ++++++++-
 clang/lib/Sema/SemaExceptionSpec.cpp          |   2 +
 clang/lib/Sema/SemaOpenMP.cpp                 | 190 +++++++++++++--
 clang/lib/Sema/TreeTransform.h                |  64 +++++
 clang/lib/Serialization/ASTReaderStmt.cpp     |  23 ++
 clang/lib/Serialization/ASTWriterStmt.cpp     |  17 ++
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |   2 +
 ...riant_adjust_args_positional_ast_print.cpp | 175 ++++++++++++++
 ...ariant_adjust_args_positional_messages.cpp | 228 ++++++++++++++++++
 ...ariant_adjust_args_positional_template.cpp |  62 +++++
 .../declare_variant_clauses_messages.cpp      |  16 +-
 clang/tools/libclang/CIndex.cpp               |   4 +
 clang/tools/libclang/CXCursor.cpp             |   8 +
 35 files changed, 1270 insertions(+), 35 deletions(-)
 create mode 100644 
clang/test/OpenMP/declare_variant_adjust_args_positional_ast_print.cpp
 create mode 100644 
clang/test/OpenMP/declare_variant_adjust_args_positional_messages.cpp
 create mode 100644 
clang/test/OpenMP/declare_variant_adjust_args_positional_template.cpp

diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index 1c8d097f1beab4..1ab3635f2ea3a3 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -1691,7 +1691,19 @@ enum CXCursorKind {
    */
   CXCursor_PackIndexingExpr = 156,
 
-  CXCursor_LastExpr = CXCursor_PackIndexingExpr,
+  /**
+   * OpenMP 6.0 [5.2.1, Parameter List Items]
+   * The 'omp_num_args' identifier with an optional logical offset.
+   */
+  CXCursor_OMPNumArgsExpr = 157,
+
+  /**
+   * OpenMP 6.0 [5.2.1, Parameter List Items]
+   * A parameter range 'lb:ub', either bound of which may be omitted.
+   */
+  CXCursor_OMPArgumentRangeExpr = 158,
+
+  CXCursor_LastExpr = CXCursor_OMPArgumentRangeExpr,
 
   /* Statements */
   CXCursor_FirstStmt = 200,
diff --git a/clang/include/clang/AST/ComputeDependence.h 
b/clang/include/clang/AST/ComputeDependence.h
index 6430081a40350d..4a36588d399e17 100644
--- a/clang/include/clang/AST/ComputeDependence.h
+++ b/clang/include/clang/AST/ComputeDependence.h
@@ -100,6 +100,8 @@ class AtomicExpr;
 class ArraySectionExpr;
 class OMPArrayShapingExpr;
 class OMPIteratorExpr;
+class OMPNumArgsExpr;
+class OMPArgumentRangeExpr;
 class ObjCArrayLiteral;
 class ObjCDictionaryLiteral;
 class ObjCBoxedExpr;
@@ -200,6 +202,8 @@ ExprDependence computeDependence(AtomicExpr *E);
 ExprDependence computeDependence(ArraySectionExpr *E);
 ExprDependence computeDependence(OMPArrayShapingExpr *E);
 ExprDependence computeDependence(OMPIteratorExpr *E);
+ExprDependence computeDependence(OMPNumArgsExpr *E);
+ExprDependence computeDependence(OMPArgumentRangeExpr *E);
 
 ExprDependence computeDependence(ObjCArrayLiteral *E);
 ExprDependence computeDependence(ObjCDictionaryLiteral *E);
diff --git a/clang/include/clang/AST/ExprOpenMP.h 
b/clang/include/clang/AST/ExprOpenMP.h
index 4d3c5f54ad7dde..4c0bd0cf6974cd 100644
--- a/clang/include/clang/AST/ExprOpenMP.h
+++ b/clang/include/clang/AST/ExprOpenMP.h
@@ -296,6 +296,153 @@ class OMPIteratorExpr final
   }
 };
 
+/// OpenMP 6.0 [5.2.1, Parameter List Items]
+/// Represents the 'omp_num_args' identifier used as a bound of a parameter
+/// range, together with an optional logical offset:
+/// \code
+/// omp_num_args [ ('+' | '-') logical_offset ]
+/// \endcode
+/// 'omp_num_args' is not a declaration and is never looked up; it stands for
+/// the number of arguments at a call site, which by OpenMP 6.0 [20.1] is the
+/// number of parameters of the associated declaration plus any variadic
+/// arguments actually passed. For a non-variadic callee that is a compile-time
+/// constant, so only a variadic callee makes the value call-site dependent.
+/// The node is therefore left unresolved in the AST; whoever consumes the
+/// 'adjust_args' parameter list substitutes the value.
+class OMPNumArgsExpr final : public Expr {
+  friend class ASTStmtReader;
+  friend class ASTStmtWriter;
+
+  /// The logical offset, or null if none was written.
+  Stmt *Offset = nullptr;
+  /// Location of the 'omp_num_args' identifier.
+  SourceLocation NumArgsLoc;
+  /// Location of the '+' or '-'; invalid if there is no offset.
+  SourceLocation OpLoc;
+  /// True if the offset was written with '-'.
+  bool IsSubtraction = false;
+
+public:
+  OMPNumArgsExpr(QualType Type, SourceLocation NumArgsLoc, SourceLocation 
OpLoc,
+                 bool IsSubtraction, Expr *Offset)
+      : Expr(OMPNumArgsExprClass, Type, VK_PRValue, OK_Ordinary),
+        Offset(Offset), NumArgsLoc(NumArgsLoc), OpLoc(OpLoc),
+        IsSubtraction(IsSubtraction) {
+    setDependence(computeDependence(this));
+  }
+
+  /// Create an empty 'omp_num_args' expression.
+  explicit OMPNumArgsExpr(EmptyShell Shell)
+      : Expr(OMPNumArgsExprClass, Shell) {}
+
+  /// Gets the logical offset, or null if none was written.
+  Expr *getOffset() { return cast_or_null<Expr>(Offset); }
+  const Expr *getOffset() const { return cast_or_null<Expr>(Offset); }
+  void setOffset(Expr *E) { Offset = E; }
+
+  /// True if the offset was written with '-' rather than '+'.
+  bool isSubtraction() const { return IsSubtraction; }
+  void setIsSubtraction(bool IS) { IsSubtraction = IS; }
+
+  SourceLocation getNumArgsLoc() const { return NumArgsLoc; }
+  void setNumArgsLoc(SourceLocation L) { NumArgsLoc = L; }
+
+  SourceLocation getOperatorLoc() const { return OpLoc; }
+  void setOperatorLoc(SourceLocation L) { OpLoc = L; }
+
+  SourceLocation getBeginLoc() const LLVM_READONLY { return NumArgsLoc; }
+  SourceLocation getEndLoc() const LLVM_READONLY {
+    return Offset ? Offset->getEndLoc() : NumArgsLoc;
+  }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == OMPNumArgsExprClass;
+  }
+
+  // Iterators
+  child_range children() { return child_range(&Offset, &Offset + 1); }
+  const_child_range children() const {
+    return const_child_range(&Offset, &Offset + 1);
+  }
+};
+
+/// OpenMP 6.0 [5.2.1, Parameter List Items]
+/// Represents a parameter range, one list item that stands for every parameter
+/// position from a lower to an upper bound:
+/// \code
+/// [ lb ] ':' [ ub ]
+/// \endcode
+/// Either bound may be omitted: an omitted \c lb defaults to 1 and an omitted
+/// \c ub defaults to 'omp_num_args'. A bound may be an 'omp_num_args'
+/// expression, which is why this node's children are general expressions 
rather
+/// than integer literals.
+///
+/// The type is 'void': a range is never a value, it only ever appears as an
+/// item of an 'adjust_args' parameter list in \c OMPDeclareVariantAttr.
+class OMPArgumentRangeExpr final : public Expr {
+  friend class ASTStmtReader;
+  friend class ASTStmtWriter;
+
+  enum { LOWER_BOUND, UPPER_BOUND, NUM_SUBEXPRS };
+
+  /// The two bounds; either may be null when the bound was omitted.
+  Stmt *SubExprs[NUM_SUBEXPRS] = {nullptr, nullptr};
+  /// Location of the ':' separating the bounds.
+  SourceLocation ColonLoc;
+
+public:
+  OMPArgumentRangeExpr(QualType Type, Expr *LowerBound, SourceLocation 
ColonLoc,
+                       Expr *UpperBound)
+      : Expr(OMPArgumentRangeExprClass, Type, VK_PRValue, OK_Ordinary),
+        ColonLoc(ColonLoc) {
+    SubExprs[LOWER_BOUND] = LowerBound;
+    SubExprs[UPPER_BOUND] = UpperBound;
+    setDependence(computeDependence(this));
+  }
+
+  /// Create an empty parameter range expression.
+  explicit OMPArgumentRangeExpr(EmptyShell Shell)
+      : Expr(OMPArgumentRangeExprClass, Shell) {}
+
+  /// Gets the lower bound, or null if it was omitted (meaning 1).
+  Expr *getLowerBound() { return cast_or_null<Expr>(SubExprs[LOWER_BOUND]); }
+  const Expr *getLowerBound() const {
+    return cast_or_null<Expr>(SubExprs[LOWER_BOUND]);
+  }
+  void setLowerBound(Expr *E) { SubExprs[LOWER_BOUND] = E; }
+
+  /// Gets the upper bound, or null if it was omitted (meaning 'omp_num_args').
+  Expr *getUpperBound() { return cast_or_null<Expr>(SubExprs[UPPER_BOUND]); }
+  const Expr *getUpperBound() const {
+    return cast_or_null<Expr>(SubExprs[UPPER_BOUND]);
+  }
+  void setUpperBound(Expr *E) { SubExprs[UPPER_BOUND] = E; }
+
+  SourceLocation getColonLoc() const { return ColonLoc; }
+  void setColonLoc(SourceLocation L) { ColonLoc = L; }
+
+  SourceLocation getBeginLoc() const LLVM_READONLY {
+    return SubExprs[LOWER_BOUND] ? SubExprs[LOWER_BOUND]->getBeginLoc()
+                                 : ColonLoc;
+  }
+  SourceLocation getEndLoc() const LLVM_READONLY {
+    return SubExprs[UPPER_BOUND] ? SubExprs[UPPER_BOUND]->getEndLoc()
+                                 : ColonLoc;
+  }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == OMPArgumentRangeExprClass;
+  }
+
+  // Iterators
+  child_range children() {
+    return child_range(&SubExprs[LOWER_BOUND], &SubExprs[NUM_SUBEXPRS]);
+  }
+  const_child_range children() const {
+    return const_child_range(&SubExprs[LOWER_BOUND], &SubExprs[NUM_SUBEXPRS]);
+  }
+};
+
 } // end namespace clang
 
 #endif
diff --git a/clang/include/clang/AST/OpenMPClause.h 
b/clang/include/clang/AST/OpenMPClause.h
index 9e9295e1a0c549..645f0fe8f9281c 100644
--- a/clang/include/clang/AST/OpenMPClause.h
+++ b/clang/include/clang/AST/OpenMPClause.h
@@ -10566,6 +10566,29 @@ class OMPXBareClause : public 
OMPNoChildClause<llvm::omp::OMPC_ompx_bare> {
   OMPXBareClause() = default;
 };
 
+/// Resolve one 'adjust_args' parameter-list item to the 1-based argument
+/// positions it identifies (OpenMP 6.0 [5.2.1] p162).
+///
+/// \param Item     A named item (\c DeclRefExpr to a \c ParmVarDecl of \p FD),
+///                 a positional item (a constant integer expression), or an
+///                 \c OMPArgumentRangeExpr. Callers pass 
IgnoreParenImpCasts().
+/// \param FD       The base function the OMPDeclareVariantAttr is attached to.
+/// \param NumArgs  The value of 'omp_num_args' at the point of resolution:
+///                 \c max(FD->getNumParams(), Call->getNumArgs()) at a call
+///                 site, or \c FD->getNumParams() with no call site available
+///                 (OpenMP 6.0 [20.1] p534).
+/// \param Positions Resolved positions are appended here, ascending. Positions
+///                 outside [1, NumArgs] are silently dropped
+///                 (OpenMP 6.0 [9.6.2] p332 L1-2).
+/// \returns false if \p Item is not a resolvable item shape, or if a bound is
+///          dependent or not a constant expression.
+///
+/// Emits no diagnostics: it lives in the AST library so it can later be
+/// shared with CodeGen. Sema diagnoses separately, before calling this.
+bool resolveOMPAdjustArgsItem(const Expr *Item, const FunctionDecl *FD,
+                              unsigned NumArgs, const ASTContext &Ctx,
+                              SmallVectorImpl<unsigned> &Positions);
+
 } // namespace clang
 
 #endif // LLVM_CLANG_AST_OPENMPCLAUSE_H
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h 
b/clang/include/clang/AST/RecursiveASTVisitor.h
index 1f7c8d762e1b52..5a66c147ac8cab 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2954,6 +2954,8 @@ DEF_TRAVERSE_STMT(MatrixSubscriptExpr, {})
 DEF_TRAVERSE_STMT(ArraySectionExpr, {})
 DEF_TRAVERSE_STMT(OMPArrayShapingExpr, {})
 DEF_TRAVERSE_STMT(OMPIteratorExpr, {})
+DEF_TRAVERSE_STMT(OMPNumArgsExpr, {})
+DEF_TRAVERSE_STMT(OMPArgumentRangeExpr, {})
 
 DEF_TRAVERSE_STMT(BlockExpr, {
   TRY_TO(TraverseDecl(S->getBlockDecl()));
diff --git a/clang/include/clang/AST/TextNodeDumper.h 
b/clang/include/clang/AST/TextNodeDumper.h
index 1cdd8c37c7fc63..f2fd730d34c5d1 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -328,6 +328,7 @@ class TextNodeDumper
   void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Node);
   void VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *Node);
   void VisitOMPIteratorExpr(const OMPIteratorExpr *Node);
+  void VisitOMPNumArgsExpr(const OMPNumArgsExpr *Node);
   void VisitConceptSpecializationExpr(const ConceptSpecializationExpr *Node);
   void VisitRequiresExpr(const RequiresExpr *Node);
 
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td 
b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 6a48d74079f4ed..5c826aba1c200f 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1623,6 +1623,10 @@ def err_omp_unknown_adjust_args_op
     : Error<
           "incorrect 'adjust_args' type, expected 'need_device_ptr'%select{|, "
           "'need_device_addr',}0 or 'nothing'">;
+def err_omp_num_args_invalid_form
+    : Error<"'omp_num_args' %select{is only allowed as a bound of a parameter "
+            "range|may only be followed by '+' or '-' and a constant logical "
+            "offset}0">;
 def err_omp_declare_variant_wrong_clause : Error<
   "expected %select{'match'|'match', 'adjust_args', or 'append_args'}0 clause "
   "on 'omp declare variant' directive">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3a910c9c3f2b92..090acdfa4cac84 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12815,6 +12815,9 @@ def err_omp_instantiation_not_supported
     : Error<"instantiation of '%0' not supported yet">;
 def err_omp_adjust_arg_multiple_clauses : Error<
   "'adjust_arg' argument %0 used in multiple clauses">;
+def err_omp_adjust_args_invalid_item : Error<
+  "expected a parameter name, a parameter position, or a parameter range in "
+  "'adjust_args' clause">;
 def err_omp_clause_requires_dispatch_construct : Error<
   "'%0' clause requires 'dispatch' context selector">;
 def err_omp_append_args_with_varargs : Error<
diff --git a/clang/include/clang/Basic/StmtNodes.td 
b/clang/include/clang/Basic/StmtNodes.td
index 84804d6705d2b9..e9f64565677e9d 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -85,6 +85,8 @@ def MatrixSingleSubscriptExpr : StmtNode<Expr>;
 def MatrixSubscriptExpr : StmtNode<Expr>;
 def ArraySectionExpr : StmtNode<Expr>;
 def OMPIteratorExpr : StmtNode<Expr>;
+def OMPNumArgsExpr : StmtNode<Expr>;
+def OMPArgumentRangeExpr : StmtNode<Expr>;
 def CallExpr : StmtNode<Expr>;
 def MemberExpr : StmtNode<Expr>;
 def CastExpr : StmtNode<Expr, 1>;
diff --git a/clang/include/clang/Parse/Parser.h 
b/clang/include/clang/Parse/Parser.h
index 163aa483a84e3f..832835b27edc51 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -7053,6 +7053,17 @@ class Parser : public CodeCompletionHandler {
   bool ParseOpenMPReservedLocator(OpenMPClauseKind Kind,
                                   SemaOpenMP::OpenMPVarListDataTy &Data,
                                   const LangOptions &LangOpts);
+
+  /// Parses one bound of an OpenMP 6.0 'adjust_args' parameter range, which 
may
+  /// be 'omp_num_args' with an optional logical offset, or the whole
+  /// parameter-list item when no range colon follows it.
+  ExprResult ParseOpenMPAdjustArgsBound();
+
+  /// Parses an OpenMP 6.0 'adjust_args' parameter list, whose items may be
+  /// parameter names, positions, or ranges with optional bounds.
+  /// Returns true on error.
+  bool ParseOpenMPAdjustArgsList(SmallVectorImpl<Expr *> &Vars);
+
   /// Parses clauses with list.
   bool ParseOpenMPVarList(OpenMPDirectiveKind DKind, OpenMPClauseKind Kind,
                           SmallVectorImpl<Expr *> &Vars,
diff --git a/clang/include/clang/Sema/SemaOpenMP.h 
b/clang/include/clang/Sema/SemaOpenMP.h
index 361473140e2364..1e1870f4690844 100644
--- a/clang/include/clang/Sema/SemaOpenMP.h
+++ b/clang/include/clang/Sema/SemaOpenMP.h
@@ -1491,6 +1491,19 @@ class SemaOpenMP : public SemaBase {
                                   SourceLocation LLoc, SourceLocation RLoc,
                                   ArrayRef<OMPIteratorData> Data);
 
+  /// Called on a well-formed 'omp_num_args' expression appearing as a bound of
+  /// an 'adjust_args' parameter range. \a Offset is null if no logical offset
+  /// was written, in which case \a OpLoc is invalid.
+  ExprResult ActOnOMPNumArgsExpr(SourceLocation NumArgsLoc,
+                                 SourceLocation OpLoc, bool IsSubtraction,
+                                 Expr *Offset);
+
+  /// Called on a well-formed 'adjust_args' parameter range 'lb:ub'. Either
+  /// bound may be null, meaning 1 for \a LowerBound and 'omp_num_args' for
+  /// \a UpperBound.
+  ExprResult ActOnOMPArgumentRangeExpr(Expr *LowerBound, SourceLocation 
ColonLoc,
+                                       Expr *UpperBound);
+
   ExprResult ActOnOpenMPDimsModifier(OpenMPClauseKind Kind, int Modifier,
                                      Expr *ModifierExpr,
                                      SourceLocation ModifierLoc,
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h 
b/clang/include/clang/Serialization/ASTBitCodes.h
index 6a52a9e4fa780f..678b17b63a9ffc 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -2057,6 +2057,8 @@ enum StmtCode {
   EXPR_ARRAY_SECTION,
   EXPR_OMP_ARRAY_SHAPING,
   EXPR_OMP_ITERATOR,
+  EXPR_OMP_NUM_ARGS,
+  EXPR_OMP_ARGUMENT_RANGE,
 
   // ARC
   EXPR_OBJC_BRIDGED_CAST, // ObjCBridgedCastExpr
diff --git a/clang/lib/AST/AttrImpl.cpp b/clang/lib/AST/AttrImpl.cpp
index 7272ad0de9a2cc..c56fd284451712 100644
--- a/clang/lib/AST/AttrImpl.cpp
+++ b/clang/lib/AST/AttrImpl.cpp
@@ -14,6 +14,7 @@
 #include "clang/AST/ASTStructuralEquivalence.h"
 #include "clang/AST/Attr.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/ExprOpenMP.h"
 #include "clang/AST/Type.h"
 #include <optional>
 #include <type_traits>
@@ -209,12 +210,20 @@ void OMPDeclareVariantAttr::printPrettyPragma(
   OS << " match(" << traitInfos << ")";
 
   auto PrintExprs = [&OS, &Policy](Expr **Begin, Expr **End) {
-    for (Expr **I = Begin; I != End; ++I) {
-      assert(*I && "Expected non-null Stmt");
-      if (I != Begin)
-        OS << ",";
-      (*I)->printPretty(OS, nullptr, Policy);
+    if (Begin != End) {
+      if (const auto *Range = dyn_cast<OMPArgumentRangeExpr>(*Begin);
+          Range && !Range->getLowerBound())
+        // A first item with an omitted lower bound starts with ':', which
+        // would form a '::' token together with the adjust-op separator.
+        OS << " ";
     }
+    llvm::interleave(
+        Begin, End,
+        [&](Expr *E) {
+          assert(E && "Expected non-null Stmt");
+          E->printPretty(OS, nullptr, Policy);
+        },
+        [&OS] { OS << ","; });
   };
   if (adjustArgsNothing_size()) {
     OS << " adjust_args(nothing:";
diff --git a/clang/lib/AST/ComputeDependence.cpp 
b/clang/lib/AST/ComputeDependence.cpp
index 0fc9da18b93940..4f33ed8beca4e3 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -500,6 +500,26 @@ ExprDependence clang::computeDependence(OMPIteratorExpr 
*E) {
   return D;
 }
 
+ExprDependence clang::computeDependence(OMPNumArgsExpr *E) {
+  // The type is always 'int', so the expression is never type-dependent; only
+  // the logical offset can make it value- or instantiation-dependent.
+  if (Expr *Offset = E->getOffset())
+    return Offset->getDependence() & ~ExprDependence::Type;
+  return ExprDependence::None;
+}
+
+ExprDependence clang::computeDependence(OMPArgumentRangeExpr *E) {
+  // The type is always 'void', so the expression is never type-dependent.
+  // Either bound may be omitted, meaning 1 for the lower bound and
+  // 'omp_num_args' for the upper bound.
+  auto D = ExprDependence::None;
+  if (Expr *LB = E->getLowerBound())
+    D |= LB->getDependence();
+  if (Expr *UB = E->getUpperBound())
+    D |= UB->getDependence();
+  return D & ~ExprDependence::Type;
+}
+
 /// Compute the type-, value-, and instantiation-dependence of a
 /// declaration reference
 /// based on the declaration being referenced.
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index e501527ed9b04b..90622bde37beda 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3831,6 +3831,8 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
   case ArraySectionExprClass:
   case OMPArrayShapingExprClass:
   case OMPIteratorExprClass:
+  case OMPNumArgsExprClass:
+  case OMPArgumentRangeExprClass:
   case MemberExprClass:
   case ConditionalOperatorClass:
   case BinaryConditionalOperatorClass:
diff --git a/clang/lib/AST/ExprClassification.cpp 
b/clang/lib/AST/ExprClassification.cpp
index eebae17d7b948b..6f3cd087b2660b 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -220,6 +220,8 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const 
Expr *E) {
   case Expr::RequiresExprClass:
   case Expr::CXXReflectExprClass:
   case Expr::CXXExpansionSelectExprClass:
+  case Expr::OMPNumArgsExprClass:
+  case Expr::OMPArgumentRangeExprClass:
     return Cl::CL_PRValue;
 
   case Expr::EmbedExprClass:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index d55749100658f7..034142aac4c706 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -22398,6 +22398,8 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext 
&Ctx) {
   case Expr::ArraySectionExprClass:
   case Expr::OMPArrayShapingExprClass:
   case Expr::OMPIteratorExprClass:
+  case Expr::OMPNumArgsExprClass:
+  case Expr::OMPArgumentRangeExprClass:
   case Expr::CompoundAssignOperatorClass:
   case Expr::CompoundLiteralExprClass:
   case Expr::ExtVectorElementExprClass:
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 7fb162a68fe886..64d81b17ed278e 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -5013,6 +5013,8 @@ void CXXNameMangler::mangleExpression(const Expr *E, 
unsigned Arity,
   case Expr::ArraySectionExprClass:
   case Expr::OMPArrayShapingExprClass:
   case Expr::OMPIteratorExprClass:
+  case Expr::OMPNumArgsExprClass:
+  case Expr::OMPArgumentRangeExprClass:
   case Expr::CXXInheritedCtorInitExprClass:
   case Expr::CXXParenListInitExprClass:
   case Expr::CXXExpansionSelectExprClass:
diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp
index cac701994def82..7e471d25c76b44 100644
--- a/clang/lib/AST/OpenMPClause.cpp
+++ b/clang/lib/AST/OpenMPClause.cpp
@@ -3269,3 +3269,91 @@ bool TargetOMPContext::matchesISATrait(StringRef 
RawString) const {
     DiagUnknownTrait(RawString);
   return false;
 }
+
+/// Evaluate one bound of an 'adjust_args' parameter range: either an
+/// 'omp_num_args [+- logical_offset]' expression, or a plain constant integer
+/// expression. Returns false if the bound is dependent or not constant.
+static bool evalOMPAdjustArgsBound(const Expr *Bound, unsigned NumArgs,
+                                   const ASTContext &Ctx, int64_t &Result) {
+  Bound = Bound->IgnoreParenImpCasts();
+  if (const auto *NumArgsExpr = dyn_cast<OMPNumArgsExpr>(Bound)) {
+    int64_t Offset = 0;
+    if (const Expr *OffsetExpr = NumArgsExpr->getOffset()) {
+      if (OffsetExpr->isValueDependent())
+        return false;
+      std::optional<llvm::APSInt> Val =
+          OffsetExpr->getIntegerConstantExpr(Ctx);
+      if (!Val)
+        return false;
+      Offset = Val->getExtValue();
+    }
+    Result = static_cast<int64_t>(NumArgs) +
+             (NumArgsExpr->isSubtraction() ? -Offset : Offset);
+    return true;
+  }
+  if (Bound->isValueDependent())
+    return false;
+  std::optional<llvm::APSInt> Val = Bound->getIntegerConstantExpr(Ctx);
+  if (!Val)
+    return false;
+  Result = Val->getExtValue();
+  return true;
+}
+
+bool clang::resolveOMPAdjustArgsItem(const Expr *Item, const FunctionDecl *FD,
+                                     unsigned NumArgs, const ASTContext &Ctx,
+                                     SmallVectorImpl<unsigned> &Positions) {
+  auto AppendIfInRange = [&](int64_t Pos) {
+    if (Pos >= 1 && Pos <= static_cast<int64_t>(NumArgs))
+      Positions.push_back(static_cast<unsigned>(Pos));
+  };
+
+  // A named parameter list item.
+  if (const auto *DRE = dyn_cast<DeclRefExpr>(Item)) {
+    const auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl());
+    if (!PVD)
+      return false;
+    unsigned Index = PVD->getFunctionScopeIndex();
+    if (FD->getNumParams() > Index &&
+        FD->getParamDecl(Index)->getCanonicalDecl() ==
+            PVD->getCanonicalDecl())
+      AppendIfInRange(static_cast<int64_t>(Index) + 1);
+    return true;
+  }
+
+  // A parameter range 'lb:ub'. An omitted lb defaults to 1, an omitted ub to
+  // 'NumArgs' (OpenMP 6.0 [5.2.1] p163).
+  if (const auto *Range = dyn_cast<OMPArgumentRangeExpr>(Item)) {
+    int64_t Lower = 1;
+    if (const Expr *LB = Range->getLowerBound()) {
+      if (!evalOMPAdjustArgsBound(LB, NumArgs, Ctx, Lower))
+        return false;
+    }
+    int64_t Upper = NumArgs;
+    if (const Expr *UB = Range->getUpperBound()) {
+      if (!evalOMPAdjustArgsBound(UB, NumArgs, Ctx, Upper))
+        return false;
+    }
+    // Clamp before looping, not just inside it: an out-of-range literal bound
+    // (e.g. 'omp_num_args-1:9223372036854775807') must not turn this into an
+    // unbounded loop.
+    Lower = std::max<int64_t>(Lower, 1);
+    Upper = std::min<int64_t>(Upper, NumArgs);
+    for (int64_t Pos = Lower; Pos <= Upper; ++Pos)
+      AppendIfInRange(Pos);
+    return true;
+  }
+
+  // The position of a parameter, given as a constant integer expression.
+  if (Item->getType()->isIntegerType()) {
+    if (Item->isValueDependent())
+      return false;
+    std::optional<llvm::APSInt> Val = Item->getIntegerConstantExpr(Ctx);
+    if (!Val)
+      return false;
+    AppendIfInRange(Val->getExtValue());
+    return true;
+  }
+
+  return false;
+}
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index e957b03b7e8985..3349f2533c8511 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -1814,6 +1814,26 @@ void StmtPrinter::VisitOMPIteratorExpr(OMPIteratorExpr 
*Node) {
   OS << ")";
 }
 
+void StmtPrinter::VisitOMPNumArgsExpr(OMPNumArgsExpr *Node) {
+  OS << "omp_num_args";
+  if (Expr *Offset = Node->getOffset()) {
+    // No surrounding spaces, so that the sign of the logical offset cannot be
+    // misread as part of a neighbouring parameter range colon.
+    OS << (Node->isSubtraction() ? "-" : "+");
+    PrintExpr(Offset);
+  }
+}
+
+void StmtPrinter::VisitOMPArgumentRangeExpr(OMPArgumentRangeExpr *Node) {
+  // An omitted bound is simply not printed: `lb:`, `:ub` and `:` are all
+  // spellings the parser accepts.
+  if (Expr *LowerBound = Node->getLowerBound())
+    PrintExpr(LowerBound);
+  OS << ":";
+  if (Expr *UpperBound = Node->getUpperBound())
+    PrintExpr(UpperBound);
+}
+
 void StmtPrinter::PrintCallArgs(CallExpr *Call) {
   for (unsigned i = 0, e = Call->getNumArgs(); i != e; ++i) {
     if (isa<CXXDefaultArgExpr>(Call->getArg(i))) {
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 08b42c1477664b..c06a1e07def8b6 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -1594,6 +1594,19 @@ void StmtProfiler::VisitOMPIteratorExpr(const 
OMPIteratorExpr *S) {
     VisitDecl(S->getIteratorDecl(I));
 }
 
+void StmtProfiler::VisitOMPNumArgsExpr(const OMPNumArgsExpr *S) {
+  VisitExpr(S);
+  // The sign is not recoverable from the children: 'omp_num_args+1' and
+  // 'omp_num_args-1' have the same offset expression.
+  ID.AddBoolean(S->isSubtraction());
+}
+
+void StmtProfiler::VisitOMPArgumentRangeExpr(const OMPArgumentRangeExpr *S) {
+  // Both bounds are children, and an omitted bound is a null child, so 'lb:'
+  // and ':ub' profile differently.
+  VisitExpr(S);
+}
+
 void StmtProfiler::VisitCallExpr(const CallExpr *S) {
   VisitExpr(S);
 }
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index f58cc4f5761b7b..8afeb97f01042e 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -2051,6 +2051,12 @@ void TextNodeDumper::VisitOMPIteratorExpr(const 
OMPIteratorExpr *Node) {
   }
 }
 
+void TextNodeDumper::VisitOMPNumArgsExpr(const OMPNumArgsExpr *Node) {
+  // The offset itself is dumped as a child; only its sign needs printing.
+  if (Node->getOffset())
+    OS << " '" << (Node->isSubtraction() ? '-' : '+') << "'";
+}
+
 void TextNodeDumper::VisitConceptSpecializationExpr(
     const ConceptSpecializationExpr *Node) {
   OS << " ";
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index 30b6c64e69f4c0..170b3955439d4f 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -4805,6 +4805,99 @@ bool Parser::ParseOpenMPReservedLocator(OpenMPClauseKind 
Kind,
   return false;
 }
 
+ExprResult Parser::ParseOpenMPAdjustArgsBound() {
+  // 'omp_num_args' is recognised by spelling: OpenMP 6.0 [5.2.1] gives it no
+  // declaration, so it is never looked up.
+  if (Tok.isNot(tok::identifier) ||
+      !Tok.getIdentifierInfo()->isStr("omp_num_args"))
+    return ParseAssignmentExpression();
+
+  SourceLocation NumArgsLoc = ConsumeToken();
+  SourceLocation OpLoc;
+  bool IsSubtraction = false;
+  ExprResult Offset;
+  if (Tok.isOneOf(tok::plus, tok::minus)) {
+    IsSubtraction = Tok.is(tok::minus);
+    OpLoc = ConsumeToken();
+    // OpenMP 6.0 [5.2.1]: the logical offset is a constant expression.
+    Offset = ParseConstantExpression();
+    if (Offset.isInvalid())
+      return ExprError();
+  }
+  // 'omp_num_args' is a whole bound, not an operand of a larger expression, so
+  // nothing else may follow it.
+  if (!Tok.isOneOf(tok::colon, tok::comma, tok::r_paren,
+                   tok::annot_pragma_openmp_end)) {
+    Diag(Tok, diag::err_omp_num_args_invalid_form) << 1;
+    return ExprError();
+  }
+  return Actions.OpenMP().ActOnOMPNumArgsExpr(NumArgsLoc, OpLoc, IsSubtraction,
+                                              Offset.get());
+}
+
+bool Parser::ParseOpenMPAdjustArgsList(SmallVectorImpl<Expr *> &Vars) {
+  bool IsError = false;
+  while (true) {
+    // An omitted lower bound stands for 1, and is written as a leading ':'.
+    ExprResult LowerBound;
+    if (Tok.isNot(tok::colon)) {
+      LowerBound = ParseOpenMPAdjustArgsBound();
+      if (!LowerBound.isUsable()) {
+        IsError = true;
+        SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
+                  StopBeforeMatch);
+      }
+    }
+
+    if (Tok.is(tok::colon)) {
+      SourceLocation ColonLoc = ConsumeToken();
+      // An omitted upper bound stands for 'omp_num_args'.
+      ExprResult UpperBound;
+      if (!Tok.isOneOf(tok::comma, tok::r_paren,
+                       tok::annot_pragma_openmp_end)) {
+        UpperBound = ParseOpenMPAdjustArgsBound();
+        if (!UpperBound.isUsable()) {
+          IsError = true;
+          SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
+                    StopBeforeMatch);
+        }
+      }
+      Vars.push_back(Actions.OpenMP()
+                         .ActOnOMPArgumentRangeExpr(LowerBound.get(), ColonLoc,
+                                                     UpperBound.get())
+                         .get());
+    } else if (LowerBound.isUsable()) {
+      // Without a colon the item is a parameter name or a position, and is
+      // pushed unchanged so that pre-6.0 lists keep their exact AST shape.
+      if (isa<OMPNumArgsExpr>(LowerBound.get())) {
+        Diag(LowerBound.get()->getBeginLoc(),
+             diag::err_omp_num_args_invalid_form)
+            << 0;
+        IsError = true;
+      } else {
+        Vars.push_back(LowerBound.get());
+      }
+    }
+
+    // Separator handling mirrors the shared var-list loop, so that a malformed
+    // item such as '1:2:3' diagnoses instead of silently mis-parsing.
+    if (Tok.isNot(tok::comma)) {
+      if (Tok.isNot(tok::r_paren) &&
+          Tok.isNot(tok::annot_pragma_openmp_end)) {
+        Diag(Tok, diag::err_omp_expected_punc)
+            << getOpenMPClauseName(OMPC_adjust_args) << /*IsDirective=*/0;
+        IsError = true;
+        SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
+                  StopBeforeMatch);
+      }
+      if (Tok.isNot(tok::comma))
+        break;
+    }
+    ConsumeToken();
+  }
+  return IsError;
+}
+
 /// Parse step size expression. Returns true if parsing is successfull,
 /// otherwise returns false.
 static bool parseStepSize(Parser &P, SemaOpenMP::OpenMPVarListDataTy &Data,
@@ -4927,6 +5020,11 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind 
DKind,
   bool HasIterator = false;
   bool InvalidIterator = false;
   bool NeedRParenForLinear = false;
+  // Set when the OpenMP 6.0 'adjust_args' parameter list has already been
+  // parsed by ParseOpenMPAdjustArgsList, so the shared list loop must be
+  // skipped.
+  bool ParsedAdjustArgsList = false;
+  bool InvalidAdjustArgsList = false;
   BalancedDelimiterTracker LinearT(*this, tok::l_paren,
                                    tok::annot_pragma_openmp_end);
   // Handle reduction-identifier for reduction clause.
@@ -5311,6 +5409,14 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind 
DKind,
       }
       ExpectAndConsume(tok::colon, diag::warn_pragma_expected_colon,
                        "adjust-op");
+      // OpenMP 6.0 [5.2.1, Parameter List Items] adds positions and parameter
+      // ranges, which the shared list loop below cannot express because a 
range
+      // colon terminates it.
+      if (getLangOpts().OpenMP >= 60 &&
+          Data.ExtraModifier != OMPC_ADJUST_ARGS_unknown) {
+        ParsedAdjustArgsList = true;
+        InvalidAdjustArgsList = ParseOpenMPAdjustArgsList(Vars);
+      }
     }
   } else if (Kind == OMPC_use_device_ptr) {
     // Handle optional fallback modifier for use_device_ptr clause.
@@ -5413,8 +5519,9 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind,
       (Kind == OMPC_adjust_args &&
        Data.ExtraModifier != OMPC_ADJUST_ARGS_unknown);
   const bool MayHaveTail = (Kind == OMPC_linear || Kind == OMPC_aligned);
-  while (IsComma || (Tok.isNot(tok::r_paren) && Tok.isNot(tok::colon) &&
-                     Tok.isNot(tok::annot_pragma_openmp_end))) {
+  while (!ParsedAdjustArgsList &&
+         (IsComma || (Tok.isNot(tok::r_paren) && Tok.isNot(tok::colon) &&
+                      Tok.isNot(tok::annot_pragma_openmp_end)))) {
     ParseScope OMPListScope(this, Scope::OpenMPDirectiveScope);
     ColonProtectionRAIIObject ColonRAII(*this, MayHaveTail);
     if (!ParseOpenMPReservedLocator(Kind, Data, getLangOpts())) {
@@ -5534,7 +5641,8 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind,
   return (Kind != OMPC_depend && Kind != OMPC_doacross && Kind != OMPC_map &&
           Vars.empty()) ||
          (MustHaveTail && !Data.DepModOrTailExpr && StepFound) ||
-         InvalidReductionId || IsInvalidMapperModifier || InvalidIterator;
+         InvalidReductionId || IsInvalidMapperModifier || InvalidIterator ||
+         InvalidAdjustArgsList;
 }
 
 OMPClause *Parser::ParseOpenMPVarListClause(OpenMPDirectiveKind DKind,
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp 
b/clang/lib/Sema/SemaExceptionSpec.cpp
index 8f08e17276ef38..fb7b1d109cf5c7 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1323,6 +1323,8 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Expr::ArraySectionExprClass:
   case Expr::OMPArrayShapingExprClass:
   case Expr::OMPIteratorExprClass:
+  case Expr::OMPNumArgsExprClass:
+  case Expr::OMPArgumentRangeExprClass:
   case Expr::BinaryOperatorClass:
   case Expr::DependentCoawaitExprClass:
   case Expr::CompoundAssignOperatorClass:
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index b39dd853ab378b..f32e37d85c11bb 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -7900,6 +7900,67 @@ static bool checkPreferTypeArgs(SemaOpenMP &S, const 
OMPInteropInfo &Info) {
   return true;
 }
 
+enum class OMPAdjustArgsVal { Known, Dependent, Invalid };
+
+/// Check one 'adjust_args' integer expression for the constant property plus
+/// either the positive or the non-negative property (OpenMP 6.0 [5.2.1] p162
+/// L32-33, p163 L1).
+static OMPAdjustArgsVal checkOMPAdjustArgsValue(SemaOpenMP &S, Expr *E,
+                                                bool StrictlyPositive,
+                                                llvm::APSInt &Result) {
+  if (E->isValueDependent() || E->isTypeDependent() ||
+      E->isInstantiationDependent())
+    return OMPAdjustArgsVal::Dependent; // re-checked on instantiation
+  if (S.SemaRef.VerifyIntegerConstantExpression(E, &Result).isInvalid())
+    return OMPAdjustArgsVal::Invalid; // already diagnosed
+  if (StrictlyPositive ? !Result.isStrictlyPositive() : Result.isNegative()) {
+    S.Diag(E->getExprLoc(), diag::err_omp_negative_expression_in_clause)
+        << getOpenMPClauseNameForDiag(OMPC_adjust_args)
+        << (StrictlyPositive ? 1 : 0) << E->getSourceRange();
+    return OMPAdjustArgsVal::Invalid;
+  }
+  return OMPAdjustArgsVal::Known;
+}
+
+/// Check both bounds of a parameter range 'lb:ub' (OpenMP 6.0 [5.2.1]
+/// p162-163). A plain bound has the positive property, checked below via
+/// checkOMPAdjustArgsValue. An 'omp_num_args' bound's logical_offset has the
+/// non-negative property instead; its constant property was already checked
+/// when it was parsed (ActOnOMPNumArgsExpr), so only the sign is checked here
+/// via a direct read of the already-verified value.
+///
+/// Deliberately does not diagnose 'lb > ub': an empty range specifies no
+/// parameters, which OpenMP 6.0 [5.2.1] p163's "as if specified individually"
+/// makes harmless, and the spec places no restriction on it.
+static bool checkOMPAdjustArgsRange(SemaOpenMP &S,
+                                    OMPArgumentRangeExpr *Range) {
+  for (Expr *Bound : {Range->getLowerBound(), Range->getUpperBound()}) {
+    if (!Bound)
+      continue;
+    if (auto *NumArgsExpr =
+            dyn_cast<OMPNumArgsExpr>(Bound->IgnoreParenImpCasts())) {
+      Expr *Offset = NumArgsExpr->getOffset();
+      if (!Offset || Offset->isValueDependent())
+        continue;
+      std::optional<llvm::APSInt> Val =
+          Offset->getIntegerConstantExpr(S.getASTContext());
+      if (Val && Val->isNegative()) {
+        S.Diag(Offset->getExprLoc(),
+               diag::err_omp_negative_expression_in_clause)
+            << getOpenMPClauseNameForDiag(OMPC_adjust_args) << 0
+            << Offset->getSourceRange();
+        return false;
+      }
+      continue;
+    }
+    llvm::APSInt Result;
+    if (checkOMPAdjustArgsValue(S, Bound, /*StrictlyPositive=*/true, Result) ==
+        OMPAdjustArgsVal::Invalid)
+      return false;
+  }
+  return true;
+}
+
 void SemaOpenMP::ActOnOpenMPDeclareVariantDirective(
     FunctionDecl *FD, Expr *VariantRef, OMPTraitInfo &TI,
     ArrayRef<Expr *> AdjustArgsNothing,
@@ -7934,20 +7995,26 @@ void SemaOpenMP::ActOnOpenMPDeclareVariantDirective(
     }
   }
 
-  // OpenMP 5.1 [2.3.5, declare variant directive, Restrictions]
-  // Each argument can only appear in a single adjust_args clause for each
-  // declare variant directive.
-  llvm::SmallPtrSet<const VarDecl *, 4> AdjustVars;
+  // OpenMP 6.0 [5.2.1] Restrictions (p162): unless otherwise specified, any
+  // given parameter list item can only be specified once across all clauses
+  // of the same type in a given directive. The subject is the *item*, not the
+  // parameter it identifies, so a range never participates in the check below
+  // and a name does not collide with a position that happens to resolve to
+  // it. In 5.1 this restriction lived in the declare variant directive's own
+  // restrictions and was worded per-argument rather than per-item.
+  llvm::SmallPtrSet<const VarDecl *, 4> AdjustVars;  // named items
+  llvm::SmallSet<uint64_t, 4> AdjustPositions;       // literal positional 
items
 
   for (Expr *E : AllAdjustArgs) {
-    E = E->IgnoreParenImpCasts();
-    if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
+    Expr *Item = E->IgnoreParenImpCasts();
+
+    // A named parameter list item — unchanged from OpenMP 5.1.
+    if (const auto *DRE = dyn_cast<DeclRefExpr>(Item)) {
       if (const auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) {
         const VarDecl *CanonPVD = PVD->getCanonicalDecl();
         if (FD->getNumParams() > PVD->getFunctionScopeIndex() &&
             FD->getParamDecl(PVD->getFunctionScopeIndex())
                     ->getCanonicalDecl() == CanonPVD) {
-          // It's a parameter of the function, check duplicates.
           if (!AdjustVars.insert(CanonPVD).second) {
             Diag(DRE->getLocation(), diag::err_omp_adjust_arg_multiple_clauses)
                 << PVD;
@@ -7957,23 +8024,76 @@ void SemaOpenMP::ActOnOpenMPDeclareVariantDirective(
         }
       }
     }
-    // Anything that is not a function parameter is an error.
-    Diag(E->getExprLoc(), diag::err_omp_param_or_this_in_clause) << FD << 0;
+
+    if (getLangOpts().OpenMP < 60) {
+      // Anything that is not a function parameter is an error before 6.0.
+      Diag(Item->getExprLoc(), diag::err_omp_param_or_this_in_clause)
+          << FD << 0;
+      return;
+    }
+
+    // OpenMP 6.0 [5.2.1] p162 L32-33: a parameter range 'lb:ub'. A range is
+    // exempt from the duplicate restriction above — it is one item
+    // identifying one or more parameters — so nothing is recorded for dedup.
+    if (auto *Range = dyn_cast<OMPArgumentRangeExpr>(Item)) {
+      if (!checkOMPAdjustArgsRange(*this, Range))
+        return;
+      continue;
+    }
+
+    // A bare 'omp_num_args' is rejected by the parser (ParseOpenMPAdjustArgs-
+    // List), so it can only ever reach here as a range bound, already handled
+    // above.
+    assert(!isa<OMPNumArgsExpr>(Item) && "bare omp_num_args reached Sema");
+
+    // OpenMP 6.0 [5.2.1] p162 L30-31: the position of a parameter, given as a
+    // positive constant integer expression. A dependent item is skipped here
+    // and rechecked when the template is instantiated.
+    if (Item->getType()->isIntegerType()) {
+      llvm::APSInt Pos;
+      switch (checkOMPAdjustArgsValue(*this, Item, /*StrictlyPositive=*/true,
+                                      Pos)) {
+      case OMPAdjustArgsVal::Invalid:
+        return;
+      case OMPAdjustArgsVal::Dependent:
+        continue;
+      case OMPAdjustArgsVal::Known:
+        if (!AdjustPositions.insert(Pos.getZExtValue()).second) {
+          Diag(Item->getExprLoc(), diag::err_omp_adjust_arg_multiple_clauses)
+              << static_cast<unsigned>(Pos.getZExtValue());
+          return;
+        }
+        continue;
+      }
+    }
+
+    // Not a name, a range, or a position: none of the three forms OpenMP 6.0
+    // [5.2.1] p162 L28-33 allows.
+    Diag(Item->getExprLoc(), diag::err_omp_adjust_args_invalid_item);
     return;
   }
 
-  // OpenMP 6.0 [9.6.2 (page 332, line 31-33, adjust_args clause, Restrictions]
-  // If the `need_device_addr` adjust-op modifier is present, each list item
-  // that appears in the clause must refer to an argument in the declaration of
-  // the function variant that has a reference type
+  // OpenMP 6.0 [9.6.2] p332 L31-33: if the need_device_addr adjust-op modifier
+  // is present, each list item that appears in the clause must refer to an
+  // argument in the declaration of the function variant that has a reference
+  // type. Unlike the need_device_ptr restriction, this one is not scoped to
+  // named items ("that refers to a specific named argument" is absent here),
+  // so positions and ranges are checked too, via the shared item-to-positions
+  // resolver.
   if (getLangOpts().OpenMP >= 60) {
     for (Expr *E : AdjustArgsNeedDeviceAddr) {
-      E = E->IgnoreParenImpCasts();
-      if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
-        if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
-          if (!VD->getType()->isReferenceType())
-            Diag(E->getExprLoc(),
-                 diag::err_omp_non_by_ref_need_device_addr_modifier_argument);
+      SmallVector<unsigned, 8> Positions;
+      // With no call site in hand, 'omp_num_args' is the declared parameter
+      // count (OpenMP 6.0 [20.1] p534). Positions past it denote variadic
+      // actuals, which have no declared parameter to check against, and are
+      // dropped by the resolver (OpenMP 6.0 [9.6.2] p332 L1-2).
+      resolveOMPAdjustArgsItem(E->IgnoreParenImpCasts(), FD, 
FD->getNumParams(),
+                               getASTContext(), Positions);
+      for (unsigned Pos : Positions) {
+        if (!FD->getParamDecl(Pos - 1)->getType()->isReferenceType()) {
+          Diag(E->getExprLoc(),
+               diag::err_omp_non_by_ref_need_device_addr_modifier_argument);
+          break; // One diagnostic per written item, not per swept position.
         }
       }
     }
@@ -26968,6 +27088,38 @@ ExprResult SemaOpenMP::ActOnOMPIteratorExpr(Scope *S,
                                  LLoc, RLoc, ID, Helpers);
 }
 
+ExprResult SemaOpenMP::ActOnOMPNumArgsExpr(SourceLocation NumArgsLoc,
+                                           SourceLocation OpLoc,
+                                           bool IsSubtraction, Expr *Offset) {
+  ASTContext &Context = getASTContext();
+  // OpenMP 6.0 [5.2.1, Parameter List Items]
+  // The logical offset is a constant integer expression. Leave a dependent
+  // offset alone; it is rechecked when the template is instantiated.
+  if (Offset && !Offset->isValueDependent()) {
+    ExprResult Res =
+        SemaRef.VerifyIntegerConstantExpression(Offset, /*Result=*/nullptr);
+    if (Res.isInvalid())
+      return ExprError();
+    Offset = Res.get();
+  }
+  // The value stands for a number of arguments, so the expression is an int
+  // even though it cannot be evaluated until the parameter list is consumed.
+  return new (Context) OMPNumArgsExpr(Context.IntTy, NumArgsLoc, OpLoc,
+                                      IsSubtraction, Offset);
+}
+
+ExprResult SemaOpenMP::ActOnOMPArgumentRangeExpr(Expr *LowerBound,
+                                                 SourceLocation ColonLoc,
+                                                 Expr *UpperBound) {
+  ASTContext &Context = getASTContext();
+  // Whether the bounds denote parameters that actually exist depends on the
+  // function the enclosing 'adjust_args' clause is attached to, so it is
+  // checked in ActOnOpenMPDeclareVariantDirective rather than here. A range is
+  // never a value, hence 'void'.
+  return new (Context)
+      OMPArgumentRangeExpr(Context.VoidTy, LowerBound, ColonLoc, UpperBound);
+}
+
 /// Check if \p AssumptionStr is a known assumption and warn if not.
 static void checkOMPAssumeAttr(Sema &S, SourceLocation Loc,
                                StringRef AssumptionStr) {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 4799f72dd61774..28d83cce1f78a2 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -2947,6 +2947,28 @@ class TreeTransform {
         /*Scope=*/nullptr, IteratorKwLoc, LLoc, RLoc, Data);
   }
 
+  /// Build a new 'omp_num_args' expression.
+  ///
+  /// By default, performs semantic analysis to build the new expression.
+  /// Subclasses may override this routine to provide different behavior.
+  ExprResult RebuildOMPNumArgsExpr(SourceLocation NumArgsLoc,
+                                   SourceLocation OpLoc, bool IsSubtraction,
+                                   Expr *Offset) {
+    return getSema().OpenMP().ActOnOMPNumArgsExpr(NumArgsLoc, OpLoc,
+                                                  IsSubtraction, Offset);
+  }
+
+  /// Build a new parameter range expression.
+  ///
+  /// By default, performs semantic analysis to build the new expression.
+  /// Subclasses may override this routine to provide different behavior.
+  ExprResult RebuildOMPArgumentRangeExpr(Expr *LowerBound,
+                                         SourceLocation ColonLoc,
+                                         Expr *UpperBound) {
+    return getSema().OpenMP().ActOnOMPArgumentRangeExpr(LowerBound, ColonLoc,
+                                                        UpperBound);
+  }
+
   /// Build a new call expression.
   ///
   /// By default, performs semantic analysis to build the new expression.
@@ -14026,6 +14048,48 @@ 
TreeTransform<Derived>::TransformOMPIteratorExpr(OMPIteratorExpr *E) {
   return Res;
 }
 
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformOMPNumArgsExpr(OMPNumArgsExpr *E) {
+  ExprResult Offset;
+  if (E->getOffset()) {
+    Offset = getDerived().TransformExpr(E->getOffset());
+    if (Offset.isInvalid())
+      return ExprError();
+  }
+
+  if (!getDerived().AlwaysRebuild() && Offset.get() == E->getOffset())
+    return E;
+
+  return getDerived().RebuildOMPNumArgsExpr(
+      E->getNumArgsLoc(), E->getOperatorLoc(), E->isSubtraction(), 
Offset.get());
+}
+
+template <typename Derived>
+ExprResult
+TreeTransform<Derived>::TransformOMPArgumentRangeExpr(OMPArgumentRangeExpr *E) 
{
+  ExprResult LowerBound;
+  if (E->getLowerBound()) {
+    LowerBound = getDerived().TransformExpr(E->getLowerBound());
+    if (LowerBound.isInvalid())
+      return ExprError();
+  }
+
+  ExprResult UpperBound;
+  if (E->getUpperBound()) {
+    UpperBound = getDerived().TransformExpr(E->getUpperBound());
+    if (UpperBound.isInvalid())
+      return ExprError();
+  }
+
+  if (!getDerived().AlwaysRebuild() &&
+      LowerBound.get() == E->getLowerBound() &&
+      UpperBound.get() == E->getUpperBound())
+    return E;
+
+  return getDerived().RebuildOMPArgumentRangeExpr(
+      LowerBound.get(), E->getColonLoc(), UpperBound.get());
+}
+
 template<typename Derived>
 ExprResult
 TreeTransform<Derived>::TransformCallExpr(CallExpr *E) {
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp 
b/clang/lib/Serialization/ASTReaderStmt.cpp
index 92c555dc427b3d..dace5720f038d8 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1063,6 +1063,21 @@ void ASTStmtReader::VisitOMPIteratorExpr(OMPIteratorExpr 
*E) {
   }
 }
 
+void ASTStmtReader::VisitOMPNumArgsExpr(OMPNumArgsExpr *E) {
+  VisitExpr(E);
+  E->setOffset(Record.readSubExpr());
+  E->setNumArgsLoc(readSourceLocation());
+  E->setOperatorLoc(readSourceLocation());
+  E->setIsSubtraction(Record.readInt() != 0);
+}
+
+void ASTStmtReader::VisitOMPArgumentRangeExpr(OMPArgumentRangeExpr *E) {
+  VisitExpr(E);
+  E->setLowerBound(Record.readSubExpr());
+  E->setUpperBound(Record.readSubExpr());
+  E->setColonLoc(readSourceLocation());
+}
+
 void ASTStmtReader::VisitCallExpr(CallExpr *E) {
   VisitExpr(E);
 
@@ -3395,6 +3410,14 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
                                        Record[ASTStmtReader::NumExprFields]);
       break;
 
+    case EXPR_OMP_NUM_ARGS:
+      S = new (Context) OMPNumArgsExpr(Empty);
+      break;
+
+    case EXPR_OMP_ARGUMENT_RANGE:
+      S = new (Context) OMPArgumentRangeExpr(Empty);
+      break;
+
     case EXPR_CALL: {
       auto NumArgs = Record[ASTStmtReader::NumExprFields];
       BitsUnpacker CallExprBits(Record[ASTStmtReader::NumExprFields + 1]);
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp 
b/clang/lib/Serialization/ASTWriterStmt.cpp
index 782fecdbd0c805..c3f60b736cdbdd 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -998,6 +998,23 @@ void ASTStmtWriter::VisitOMPIteratorExpr(OMPIteratorExpr 
*E) {
   Code = serialization::EXPR_OMP_ITERATOR;
 }
 
+void ASTStmtWriter::VisitOMPNumArgsExpr(OMPNumArgsExpr *E) {
+  VisitExpr(E);
+  Record.AddStmt(E->getOffset());
+  Record.AddSourceLocation(E->getNumArgsLoc());
+  Record.AddSourceLocation(E->getOperatorLoc());
+  Record.push_back(E->isSubtraction() ? 1 : 0);
+  Code = serialization::EXPR_OMP_NUM_ARGS;
+}
+
+void ASTStmtWriter::VisitOMPArgumentRangeExpr(OMPArgumentRangeExpr *E) {
+  VisitExpr(E);
+  Record.AddStmt(E->getLowerBound());
+  Record.AddStmt(E->getUpperBound());
+  Record.AddSourceLocation(E->getColonLoc());
+  Code = serialization::EXPR_OMP_ARGUMENT_RANGE;
+}
+
 void ASTStmtWriter::VisitCallExpr(CallExpr *E) {
   VisitExpr(E);
 
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index e6349eb4eba2ae..19c179f77402ec 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1939,6 +1939,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::ArraySectionExprClass:
     case Stmt::OMPArrayShapingExprClass:
     case Stmt::OMPIteratorExprClass:
+    case Stmt::OMPNumArgsExprClass:
+    case Stmt::OMPArgumentRangeExprClass:
     case Stmt::SYCLUniqueStableNameExprClass:
     case Stmt::OpenACCAsteriskSizeExprClass:
     case Stmt::TypeTraitExprClass: {
diff --git 
a/clang/test/OpenMP/declare_variant_adjust_args_positional_ast_print.cpp 
b/clang/test/OpenMP/declare_variant_adjust_args_positional_ast_print.cpp
new file mode 100644
index 00000000000000..7945b2dbd2679d
--- /dev/null
+++ b/clang/test/OpenMP/declare_variant_adjust_args_positional_ast_print.cpp
@@ -0,0 +1,175 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN:   -x c++ -std=c++14 -fsyntax-only -verify %s
+
+// expected-no-diagnostics
+
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN:   -x c++ -std=c++14 -ast-print %s | FileCheck %s --check-prefix=PRINT
+
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN:   -x c++ -std=c++14 -emit-pch -o %t %s
+
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN:   -x c++ -std=c++14 -include-pch %t -ast-print %s \
+// RUN:   | FileCheck %s --check-prefix=PRINT
+
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN:   -x c++ -std=c++14 -ast-dump %s | FileCheck %s --check-prefix=DUMP
+
+#ifndef HEADER
+#define HEADER
+
+void v_pos(int *A, int *B, int *C, int *D);
+void v_range(int *A, int *B, int *C, int *D);
+void v_variadic(int *A, int *B, int *C, int *D, ...);
+void v_offset(int *A, int *B, int *C, ...);
+void v_lb(int *A, int *B);
+void v_ub(int *A, int *B);
+void v_both(int *A, int *B);
+void v_named(int *A, int *B);
+void v_mixed(int *A, int *B);
+void v_cond(int *A, int *B, int *C);
+void v_addr(int &A, int &B);
+
+// A list item may be a position: a constant integer expression.
+// PRINT: #pragma omp declare variant(v_pos) match(construct={dispatch}) 
adjust_args(need_device_ptr:2,4)
+// DUMP: FunctionDecl{{.*}}pos 'void (int *, int *, int *, int *)'
+// DUMP: OMPDeclareVariantAttr
+// DUMP: IntegerLiteral{{.*}}'int' 2
+// DUMP-NEXT: IntegerLiteral{{.*}}'int' 4
+#pragma omp declare variant(v_pos) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 2, 4)
+void pos(int *A, int *B, int *C, int *D);
+
+// A list item may be a parameter range 'lb:ub'.
+// PRINT: #pragma omp declare variant(v_range) match(construct={dispatch}) 
adjust_args(need_device_ptr:1:3)
+// DUMP: FunctionDecl{{.*}}range 'void (int *, int *, int *, int *)'
+// DUMP: OMPArgumentRangeExpr{{.*}}'void'
+// DUMP-NEXT: IntegerLiteral{{.*}}'int' 1
+// DUMP-NEXT: IntegerLiteral{{.*}}'int' 3
+#pragma omp declare variant(v_range) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 1:3)
+void range(int *A, int *B, int *C, int *D);
+
+// 'omp_num_args' is spelled only as a range bound, optionally with a logical
+// offset. The offset is a constant expression, hence the ConstantExpr wrapper.
+// PRINT: #pragma omp declare variant(v_variadic) match(construct={dispatch}) 
adjust_args(need_device_ptr:1:3,5,omp_num_args-1:omp_num_args)
+// DUMP: FunctionDecl{{.*}}variadic 'void (int *, int *, int *, int *, ...)'
+// DUMP: IntegerLiteral{{.*}}'int' 5
+// DUMP-NEXT: OMPArgumentRangeExpr{{.*}}'void'
+// DUMP-NEXT: OMPNumArgsExpr{{.*}}'int' '-'
+// DUMP-NEXT: ConstantExpr{{.*}}'int'
+// DUMP: IntegerLiteral{{.*}}'int' 1
+// DUMP-NEXT: OMPNumArgsExpr{{.*}}'int'
+// DUMP-NEXT: <<<NULL>>>
+#pragma omp declare variant(v_variadic) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 1:3, 5, omp_num_args-1:omp_num_args)
+void variadic(int *A, int *B, int *C, int *D, ...);
+
+// Both signs of the logical offset are accepted. An item that resolves outside
+// the parameter list is ignored rather than diagnosed, per OpenMP 6.0 [9.6.2].
+// PRINT: #pragma omp declare variant(v_offset) match(construct={dispatch}) 
adjust_args(nothing:omp_num_args-2:omp_num_args,omp_num_args+1:)
+// DUMP: FunctionDecl{{.*}}offset 'void (int *, int *, int *, ...)'
+// DUMP: OMPNumArgsExpr{{.*}}'int' '+'
+#pragma omp declare variant(v_offset) match(construct={dispatch}) \
+  adjust_args(nothing: omp_num_args-2:omp_num_args, omp_num_args+1:)
+void offset(int *A, int *B, int *C, ...);
+
+// OpenMP 6.0 [5.2.1] p163: an omitted 'lb' stands for 1. As the first list 
item
+// it must still be separated from the adjust-op colon, or the two colons would
+// lex as a single '::' and the printed clause would not parse back.
+// PRINT: #pragma omp declare variant(v_lb) match(construct={dispatch}) 
adjust_args(need_device_ptr: :2)
+// DUMP: FunctionDecl{{.*}}lb 'void (int *, int *)'
+// DUMP: OMPArgumentRangeExpr{{.*}}'void'
+// DUMP-NEXT: <<<NULL>>>
+// DUMP-NEXT: IntegerLiteral{{.*}}'int' 2
+#pragma omp declare variant(v_lb) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: :2)
+void lb(int *A, int *B);
+
+// An omitted 'ub' stands for 'omp_num_args'.
+// PRINT: #pragma omp declare variant(v_ub) match(construct={dispatch}) 
adjust_args(need_device_ptr:1:)
+// DUMP: FunctionDecl{{.*}}ub 'void (int *, int *)'
+// DUMP: OMPArgumentRangeExpr{{.*}}'void'
+// DUMP-NEXT: IntegerLiteral{{.*}}'int' 1
+// DUMP-NEXT: <<<NULL>>>
+#pragma omp declare variant(v_ub) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 1:)
+void ub(int *A, int *B);
+
+// The two bounds are optional independently, so omitting both denotes
+// 1:omp_num_args, that is every parameter.
+// PRINT: #pragma omp declare variant(v_both) match(construct={dispatch}) 
adjust_args(need_device_ptr: :)
+// DUMP: FunctionDecl{{.*}}both 'void (int *, int *)'
+// DUMP: OMPArgumentRangeExpr{{.*}}'void'
+// DUMP-NEXT: <<<NULL>>>
+// DUMP-NEXT: <<<NULL>>>
+#pragma omp declare variant(v_both) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: :)
+void both(int *A, int *B);
+
+// The pre-6.0 named form keeps its exact spelling and AST shape.
+// PRINT: #pragma omp declare variant(v_named) match(construct={dispatch}) 
adjust_args(need_device_ptr:A,B)
+// DUMP: FunctionDecl{{.*}}named 'void (int *, int *)'
+// DUMP: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}}'A' 'int *'
+// DUMP-NEXT: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}}'B' 'int *'
+#pragma omp declare variant(v_named) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: A, B)
+void named(int *A, int *B);
+
+// Named items, positions and ranges may be mixed in one clause.
+// PRINT: #pragma omp declare variant(v_mixed) match(construct={dispatch}) 
adjust_args(need_device_ptr:A,2:2)
+// DUMP: FunctionDecl{{.*}}mixed 'void (int *, int *)'
+// DUMP: DeclRefExpr{{.*}}'int *' lvalue ParmVar{{.*}}'A' 'int *'
+// DUMP-NEXT: OMPArgumentRangeExpr{{.*}}'void'
+#pragma omp declare variant(v_mixed) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: A, 2:2)
+void mixed(int *A, int *B);
+
+// A conditional operator keeps its own colon: the item is one position, not a
+// range, so a bound is never split at a '?:' colon.
+// PRINT: #pragma omp declare variant(v_cond) match(construct={dispatch}) 
adjust_args(need_device_ptr:1 ? 2 : 3)
+// DUMP: FunctionDecl{{.*}}cond 'void (int *, int *, int *)'
+// DUMP: ConditionalOperator{{.*}}'int'
+#pragma omp declare variant(v_cond) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 1 ? 2 : 3)
+void cond(int *A, int *B, int *C);
+
+// A range does not name a parameter, so the 'need_device_addr' reference-type
+// restriction of OpenMP 6.0 [9.6.2] does not apply to it.
+// PRINT: #pragma omp declare variant(v_addr) match(construct={dispatch}) 
adjust_args(need_device_addr:1:2)
+#pragma omp declare variant(v_addr) match(construct={dispatch}) \
+  adjust_args(need_device_addr: 1:2)
+void addr(int &A, int &B);
+
+// Dependent bounds are accepted in the template pattern and rechecked on
+// instantiation.
+template <int N>
+void tmpl_v(int *A, int *B, int *C, ...);
+
+template <int N>
+void tmpl(int *A, int *B, int *C, ...);
+
+// The instantiation is dumped before the pattern it came from, so its checks
+// come first. Substituting N=2 makes the logical offset a constant expression.
+// DUMP: FunctionDecl{{.*}}tmpl 'void (int *, int *, int *, ...)' 
explicit_instantiation_definition
+// DUMP: OMPArgumentRangeExpr{{.*}}'void'
+// DUMP-NEXT: SubstNonTypeTemplateParmExpr{{.*}}'int'
+// DUMP: BinaryOperator{{.*}}'int' '+'
+// DUMP: OMPNumArgsExpr{{.*}}'int' '-'
+// DUMP-NEXT: ConstantExpr{{.*}}'int'
+// DUMP-NEXT: value: Int 2
+//
+// In the pattern the offset stays dependent, so it is not wrapped.
+// DUMP: OMPNumArgsExpr{{.*}}'int' '-'
+// DUMP-NEXT: DeclRefExpr{{.*}}'int' NonTypeTemplateParm{{.*}}'N' 'int'
+//
+// PRINT: #pragma omp declare variant(tmpl_v<N>) match(construct={dispatch}) 
adjust_args(need_device_ptr:N:N + 1,omp_num_args-N:omp_num_args)
+#pragma omp declare variant(tmpl_v<N>) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: N:N + 1, omp_num_args-N:omp_num_args)
+template <int N>
+void tmpl(int *A, int *B, int *C, ...) {}
+
+template void tmpl<2>(int *, int *, int *, ...);
+
+#endif // HEADER
diff --git 
a/clang/test/OpenMP/declare_variant_adjust_args_positional_messages.cpp 
b/clang/test/OpenMP/declare_variant_adjust_args_positional_messages.cpp
new file mode 100644
index 00000000000000..6b38d6a7f3a113
--- /dev/null
+++ b/clang/test/OpenMP/declare_variant_adjust_args_positional_messages.cpp
@@ -0,0 +1,228 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN:   -x c++ -std=c++14 -ferror-limit 100 -fsyntax-only -verify=expected %s
+
+// The same syntax must still be rejected before OpenMP 6.0, which pins that 
the
+// new parsing is version-gated.
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=51 \
+// RUN:   -x c++ -std=c++14 -ferror-limit 100 -DOMP51 -fsyntax-only \
+// RUN:   -verify=omp51 %s
+
+int G; // expected-note 2 {{declared here}}
+
+void v1(int *aaa, int *bbb, ...);
+
+#ifndef OMP51
+
+// OpenMP 6.0 [5.2.1] p162: 'omp_num_args' may only be followed by a signed
+// constant logical offset, so no other operator may continue the bound.
+// expected-error@+2 {{'omp_num_args' may only be followed by '+' or '-' and a 
constant logical offset}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: omp_num_args/2)
+void f1(int *aaa, int *bbb, ...);
+
+// expected-error@+2 {{'omp_num_args' may only be followed by '+' or '-' and a 
constant logical offset}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: omp_num_args*2)
+void f2(int *aaa, int *bbb, ...);
+
+// 'omp_num_args' is recognised by spelling, and only where a bound starts. It
+// has no declaration, so anywhere else it is just an unknown identifier.
+// expected-error@+2 {{use of undeclared identifier 'omp_num_args'}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 2*omp_num_args)
+void f3(int *aaa, int *bbb, ...);
+
+// expected-error@+2 {{use of undeclared identifier 'omp_num_args'}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: (omp_num_args):2)
+void f4(int *aaa, int *bbb, ...);
+
+// A sign must be followed by an actual offset expression.
+// expected-error@+2 {{expected expression}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: omp_num_args +)
+void f5(int *aaa, int *bbb, ...);
+
+// expected-error@+2 {{expected expression}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 1:omp_num_args-)
+void f6(int *aaa, int *bbb, ...);
+
+// The logical offset must be a constant integer expression.
+// expected-error@+3 {{expression is not an integral constant expression}}
+// expected-note@+2 {{read of non-const variable 'G' is not allowed in a 
constant expression}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: omp_num_args-G:omp_num_args)
+void f7(int *aaa, int *bbb, ...);
+
+// expected-error@+2 {{integral constant expression must have integral or 
unscoped enumeration type, not 'double'}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: omp_num_args-1.5:omp_num_args)
+void f8(int *aaa, int *bbb, ...);
+
+// OpenMP 6.0 [5.2.1] p162 lists three forms of parameter list item, and a bare
+// 'omp_num_args' is not one of them: it is legal only as a range bound.
+// expected-error@+2 {{'omp_num_args' is only allowed as a bound of a 
parameter range}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: omp_num_args)
+void f9(int *aaa, int *bbb, ...);
+
+// expected-error@+2 {{'omp_num_args' is only allowed as a bound of a 
parameter range}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: omp_num_args-1)
+void f10(int *aaa, int *bbb, ...);
+
+// The rejection is per item, not only for the first one.
+// expected-error@+2 {{'omp_num_args' is only allowed as a bound of a 
parameter range}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: aaa, omp_num_args)
+void f11(int *aaa, int *bbb, ...);
+
+// A range has exactly one colon, so a third bound is a malformed item rather
+// than a silently mis-parsed one.
+// expected-error@+2 {{expected ',' or ')' in 'adjust_args' clause}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 1:2:3)
+void f12(int *aaa, int *bbb, ...);
+
+// A non-constant, non-parameter item is still not a position: it must be a
+// constant integer expression (OpenMP 6.0 [5.2.1] p162 L30-31).
+// expected-error@+3 {{expression is not an integral constant expression}}
+// expected-note@+2 {{read of non-const variable 'G' is not allowed in a 
constant expression}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: G)
+void f13(int *aaa, int *bbb, ...);
+
+// The list must have at least one item.
+// expected-error@+2 {{expected expression}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: )
+void f14(int *aaa, int *bbb, ...);
+
+// A trailing separator starts an item that is not there.
+// expected-error@+2 {{expected expression}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 1:2,)
+void f15(int *aaa, int *bbb, ...);
+
+// A leading separator is not an omitted item either; only a colon may open 
one.
+// expected-error@+2 {{expected expression}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: , 1)
+void f16(int *aaa, int *bbb, ...);
+
+// The list loop must give up at the end of the directive rather than spin on a
+// clause that is never closed.
+// expected-error@+3 {{expected ')'}}
+// expected-note@+2 {{to match this '('}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 1:2
+void f17(int *aaa, int *bbb, ...);
+
+// A malformed item is skipped up to the separator, so it neither swallows nor
+// duplicates a diagnostic for the item that follows it.
+// expected-error@+2 {{'omp_num_args' is only allowed as a bound of a 
parameter range}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: omp_num_args, 2)
+void f18(int *aaa, int *bbb, ...);
+
+// OpenMP 6.0 [5.2.1] p162 L30-31: a position has the positive property.
+// expected-error@+2 {{argument to 'adjust_args' clause must be a strictly 
positive integer value}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 0)
+void h1(int *aaa, int *bbb, ...);
+
+// expected-error@+2 {{argument to 'adjust_args' clause must be a strictly 
positive integer value}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: -1)
+void h2(int *aaa, int *bbb, ...);
+
+// A plain range bound has the positive property too.
+// expected-error@+2 {{argument to 'adjust_args' clause must be a strictly 
positive integer value}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 0:5)
+void h3(int *aaa, int *bbb, ...);
+
+// The logical offset has the non-negative property, checked independently of
+// its already-verified constant property (OpenMP 6.0 [5.2.1] p163 L1).
+// expected-error@+2 {{argument to 'adjust_args' clause must be a non-negative 
integer value}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: omp_num_args-(-1):omp_num_args)
+void h4(int *aaa, int *bbb, ...);
+
+// The duplicate restriction (OpenMP 6.0 [5.2.1] p162) applies to positions,
+// not only to names.
+// expected-error@+2 {{'adjust_arg' argument 2 used in multiple clauses}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 2, 2)
+void h5(int *aaa, int *bbb, ...);
+
+// A name and a position that happen to resolve to the same parameter are two
+// distinct items (OpenMP 6.0 [5.2.1] p162), so this is accepted, not a
+// duplicate.
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: aaa, 1)
+void h6(int *aaa, int *bbb, ...);
+
+// Not a name, a range, or a position: none of the three forms is satisfied.
+// expected-error@+2 {{expected a parameter name, a parameter position, or a 
parameter range in 'adjust_args' clause}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 1.5)
+void h7(int *aaa, int *bbb, ...);
+
+// need_device_addr's reference-type restriction is not scoped to named items
+// (OpenMP 6.0 [9.6.2] p332 L31-33), so a position is checked too.
+// expected-error@+2 {{expected reference type argument on 'adjust_args' 
clause with 'need_device_addr' modifier}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_addr: 1)
+void h8(int *aaa, int *bbb, ...);
+
+// A huge literal upper bound must not turn range resolution into an unbounded
+// loop: out-of-range positions are dropped (OpenMP 6.0 [9.6.2] p332 L1-2), not
+// enumerated one at a time up to the written value.
+void v2(int &aaa, int &bbb, ...);
+#pragma omp declare variant(v2) match(construct={dispatch}) \
+  adjust_args(need_device_addr: 1:9223372036854775807)
+void h9(int &aaa, int &bbb, ...);
+
+// need_device_addr's reference-type check via a range covers every position
+// it sweeps, but reports only one diagnostic per written item, not one per
+// position (OpenMP 6.0 [9.6.2] p332 L31-33).
+// expected-error@+2 {{expected reference type argument on 'adjust_args' 
clause with 'need_device_addr' modifier}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_addr: 1:2)
+void h10(int *aaa, int *bbb, ...);
+
+// 'lb > ub' specifies no parameters and is accepted silently: the spec places
+// no restriction on it (OpenMP 6.0 [5.2.1] p163).
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 5:2)
+void h11(int *aaa, int *bbb, ...);
+
+#else // OMP51
+
+// Before 6.0 a range is not parsed at all: the list stops at the colon, so the
+// clause is left unterminated and the leading bound is then rejected by Sema.
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 1:2) // omp51-error {{expected ',' or ')' in 
'adjust_args' clause}} omp51-error {{expected ')'}} omp51-note {{to match this 
'('}} omp51-error {{expected reference to one of the parameters of function 
'g1'}}
+void g1(int *aaa, int *bbb, ...);
+
+// A position is not a parameter name before 6.0.
+// omp51-error@+2 {{expected reference to one of the parameters of function 
'g2'}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: 2)
+void g2(int *aaa, int *bbb, ...);
+
+// An omitted lower bound is not an item, so the list is simply empty.
+// omp51-error@+2 {{expected expression}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: :)
+void g3(int *aaa, int *bbb, ...);
+
+// 'omp_num_args' is not recognised at all, and is looked up as a name.
+// omp51-error@+2 {{use of undeclared identifier 'omp_num_args'}}
+#pragma omp declare variant(v1) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: omp_num_args-1:omp_num_args)
+void g4(int *aaa, int *bbb, ...);
+
+#endif // OMP51
diff --git 
a/clang/test/OpenMP/declare_variant_adjust_args_positional_template.cpp 
b/clang/test/OpenMP/declare_variant_adjust_args_positional_template.cpp
new file mode 100644
index 00000000000000..a6c60221e57ef6
--- /dev/null
+++ b/clang/test/OpenMP/declare_variant_adjust_args_positional_template.cpp
@@ -0,0 +1,62 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN:   -x c++ -std=c++14 -fsyntax-only -verify %s
+
+// OpenMP 6.0 [5.2.1]: a dependent parameter-list item is accepted at the
+// template definition and rechecked only once substitution makes it
+// non-dependent (S6). SemaTemplateInstantiateDecl re-invokes
+// ActOnOpenMPDeclareVariantDirective per specialization, so nothing at
+// definition time is lost by deferring.
+
+template <int N>
+void v_tmpl(int *aaa, int *bbb, ...);
+
+// No error here: 'N' is value-dependent at definition time.
+#pragma omp declare variant(v_tmpl<N>) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: N)
+template <int N>
+void tmpl_pos(int *aaa, int *bbb, ...) {}
+
+// Clean once substituted: position 2 is 'bbb', a valid, positive position.
+template void tmpl_pos<2>(int *, int *, ...);
+
+// The positive property (OpenMP 6.0 [5.2.1] p162 L30-31) is only checkable
+// once 'N' is substituted.
+// expected-error@15 {{argument to 'adjust_args' clause must be a strictly 
positive integer value}}
+// expected-note@+1 {{in instantiation of function template specialization 
'tmpl_pos<0>' requested here}}
+template void tmpl_pos<0>(int *, int *, ...);
+
+template <int N>
+void v_tmpl_range(int *aaa, int *bbb, int *ccc, ...);
+
+// A dependent range bound is accepted at definition time too.
+#pragma omp declare variant(v_tmpl_range<N>) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: N:N + 1)
+template <int N>
+void tmpl_range(int *aaa, int *bbb, int *ccc, ...) {}
+
+// Clean once substituted: range 2:3 covers 'bbb' and 'ccc'.
+template void tmpl_range<2>(int *, int *, int *, ...);
+
+// The positive property on a range bound is likewise only checkable once 'N'
+// is substituted.
+// expected-error@33 {{argument to 'adjust_args' clause must be a strictly 
positive integer value}}
+// expected-note@+1 {{in instantiation of function template specialization 
'tmpl_range<0>' requested here}}
+template void tmpl_range<0>(int *, int *, int *, ...);
+
+template <int N>
+void v_tmpl_offset(int *aaa, int *bbb, ...);
+
+// A dependent 'omp_num_args' logical offset is accepted at definition time.
+#pragma omp declare variant(v_tmpl_offset<N>) match(construct={dispatch}) \
+  adjust_args(need_device_ptr: omp_num_args-N:omp_num_args)
+template <int N>
+void tmpl_offset(int *aaa, int *bbb, ...) {}
+
+// Clean once substituted: offset 1 is non-negative.
+template void tmpl_offset<1>(int *, int *, ...);
+
+// The non-negative property on the offset (OpenMP 6.0 [5.2.1] p163 L1) is
+// likewise only checkable once 'N' is substituted.
+// expected-error@51 {{argument to 'adjust_args' clause must be a non-negative 
integer value}}
+// expected-note@+1 {{in instantiation of function template specialization 
'tmpl_offset<-1>' requested here}}
+template void tmpl_offset<-1>(int *, int *, ...);
diff --git a/clang/test/OpenMP/declare_variant_clauses_messages.cpp 
b/clang/test/OpenMP/declare_variant_clauses_messages.cpp
index 916d15fde9ff28..c09619f2b7fcc9 100644
--- a/clang/test/OpenMP/declare_variant_clauses_messages.cpp
+++ b/clang/test/OpenMP/declare_variant_clauses_messages.cpp
@@ -1,9 +1,9 @@
-// RUN: %clang_cc1 -verify -triple x86_64-unknown-linux -fopenmp 
-fopenmp-version=60 -std=c++11 -o - %s
-// RUN: %clang_cc1 -verify -triple x86_64-unknown-linux -fopenmp 
-fopenmp-version=60 -std=c++11 \
+// RUN: %clang_cc1 -verify=expected,cxx -triple x86_64-unknown-linux -fopenmp 
-fopenmp-version=60 -std=c++11 -o - %s
+// RUN: %clang_cc1 -verify=expected,cxx -triple x86_64-unknown-linux -fopenmp 
-fopenmp-version=60 -std=c++11 \
 // RUN:  -DNO_INTEROP_T_DEF -o - %s
-// RUN: %clang_cc1 -verify -triple x86_64-unknown-linux -fopenmp 
-fopenmp-version=60 -std=c++11 -o - %s
-// RUN: %clang_cc1 -verify -triple x86_64-unknown-linux -fopenmp 
-fopenmp-version=60 -Wno-strict-prototypes -DC -x c -o - %s
-// RUN: %clang_cc1 -verify -triple x86_64-pc-windows-msvc -fms-compatibility \
+// RUN: %clang_cc1 -verify=expected,cxx -triple x86_64-unknown-linux -fopenmp 
-fopenmp-version=60 -std=c++11 -o - %s
+// RUN: %clang_cc1 -verify=expected,c -triple x86_64-unknown-linux -fopenmp 
-fopenmp-version=60 -Wno-strict-prototypes -DC -x c -o - %s
+// RUN: %clang_cc1 -verify=expected,c -triple x86_64-pc-windows-msvc 
-fms-compatibility \
 // RUN:  -fopenmp -fopenmp-version=60 -Wno-strict-prototypes -DC -DWIN -x c -o 
- %s
 
 #ifdef NO_INTEROP_T_DEF
@@ -15,7 +15,7 @@ void foo_v1(float *);
 #else
 typedef void *omp_interop_t;
 
-int Other;
+int Other; // cxx-note {{declared here}}
 
 #if _OPENMP >= 202011  // At least OpenMP 5.1
 #ifdef __cplusplus
@@ -135,7 +135,9 @@ void vararg_bar2(const char *fmt) { return; }
    adjust_args(need_device_addr:AAA)                         \
    match(construct={dispatch}, device={arch(x86,x86_64)})
 
-// expected-error@+2 {{expected reference to one of the parameters of function 
'foo'}}
+// cxx-error@+4 {{expression is not an integral constant expression}}
+// cxx-note@+3 {{read of non-const variable 'Other' is not allowed in a 
constant expression}}
+// c-error@+2 {{expression is not an integer constant expression}}
 #pragma omp declare variant(foo_v3)                          \
    adjust_args(nothing:Other)                                \
    match(construct={dispatch}, device={arch(x86,x86_64)})
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 2c5e799d12f181..271fc889fb6e23 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -6043,6 +6043,10 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind 
Kind) {
     return cxstring::createRef("OMPArrayShapingExpr");
   case CXCursor_OMPIteratorExpr:
     return cxstring::createRef("OMPIteratorExpr");
+  case CXCursor_OMPNumArgsExpr:
+    return cxstring::createRef("OMPNumArgsExpr");
+  case CXCursor_OMPArgumentRangeExpr:
+    return cxstring::createRef("OMPArgumentRangeExpr");
   case CXCursor_BinaryOperator:
     return cxstring::createRef("BinaryOperator");
   case CXCursor_CompoundAssignOperator:
diff --git a/clang/tools/libclang/CXCursor.cpp 
b/clang/tools/libclang/CXCursor.cpp
index 768f5465a166a6..8ad3136bf1b95d 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -458,6 +458,14 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl 
*Parent,
     K = CXCursor_OMPIteratorExpr;
     break;
 
+  case Stmt::OMPNumArgsExprClass:
+    K = CXCursor_OMPNumArgsExpr;
+    break;
+
+  case Stmt::OMPArgumentRangeExprClass:
+    K = CXCursor_OMPArgumentRangeExpr;
+    break;
+
   case Stmt::BinaryOperatorClass:
     K = CXCursor_BinaryOperator;
     break;

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to