danielmarjamaki updated this revision to Diff 37223.
danielmarjamaki added a comment.

Minor cleanups and refactorings


http://reviews.llvm.org/D12359

Files:
  include/clang/AST/Decl.h
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Parse/Parser.h
  include/clang/Sema/Sema.h
  lib/AST/Decl.cpp
  lib/Parse/ParseExpr.cpp
  lib/Parse/ParseStmt.cpp
  lib/Sema/SemaDecl.cpp
  lib/Sema/SemaExpr.cpp
  lib/Sema/SemaLambda.cpp
  lib/Sema/SemaOpenMP.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  lib/Serialization/ASTReaderDecl.cpp
  test/Sema/warn-nonconst-parameter.c
  test/SemaCXX/warn-nonconst-parameter.cpp

Index: test/SemaCXX/warn-nonconst-parameter.cpp
===================================================================
--- test/SemaCXX/warn-nonconst-parameter.cpp
+++ test/SemaCXX/warn-nonconst-parameter.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -fsyntax-only -Wnonconst-parameter -verify %s
+//
+// Test that -Wnonconst-parameter does not warn for virtual functions.
+//
+
+// expected-no-diagnostics
+
+class Base {
+public:
+  virtual int f(int*p);
+};
+
+class Derived : public Base {
+public:
+  // Don't warn on inherited virtual function
+  virtual int f(int *p) { return 0; }
+
+  // Don't warn on non-inherited virtual function
+  virtual int g(int *p) { return 0; }
+};
+
+class Fred {
+private:
+  void *X;
+public:
+  Fred(void *X) : X(X) {}
+  void dostuff();
+};
+
+static Fred& dontwarn1(void *P) {
+  return *static_cast<Fred*>(P);
+}
+
+static void dontwarn2(void *P) {
+  ((Fred*)P)->dostuff();
+}
+
+
+// Don't warn when data is passed to nonconst reference parameter
+void dostuff(int &x);
+void dontwarn19(int *p) {
+  dostuff(*p);
+}
+
Index: test/Sema/warn-nonconst-parameter.c
===================================================================
--- test/Sema/warn-nonconst-parameter.c
+++ test/Sema/warn-nonconst-parameter.c
@@ -0,0 +1,125 @@
+// RUN: %clang_cc1 -fsyntax-only -Wnonconst-parameter -verify %s
+
+// Currently the -Wnonconst-parameter only warns about pointer arguments.
+//
+// It can be defined both that the data is const and that the pointer is const,
+// the -Wnonconst-parameter only checks if the data can be const-specified.
+//
+// It does not warn about pointers to records or function pointers.
+
+// Some external function where first argument is nonconst and second is const.
+char* strcpy1(char *dest, const char *src);
+unsigned my_strcpy(char *buf, const char *s);
+unsigned my_strlen(const char *buf);
+
+
+int warn1(int *p) { // expected-warning {{parameter 'p' can be const}}
+  return *p;
+}
+
+void warn2(int *first, int *last) { // expected-warning {{parameter 'last' can be const}}
+  *first = 0;
+  if (first < last) {} // <- last can be const
+}
+
+void warn3(char *p) { // expected-warning {{parameter 'p' can be const}}
+  char buf[10];
+  strcpy1(buf, p);
+}
+
+int dontwarn1(const int *p) {
+  return *p;
+}
+
+void dontwarn2(int *p) {
+  *p = 0;
+}
+
+void dontwarn3(char *p) {
+  p[0] = 0;
+}
+
+void dontwarn4(int *p) {
+  int *q = p;
+  *q = 0;
+}
+
+void dontwarn5(float *p) {
+  int *q = (int *)p;
+}
+
+void dontwarn6(char *p) {
+  char *a, *b;
+  a = b = p;
+}
+
+void dontwarn7(int *p) {
+  int *i = p ? p : 0;
+}
+
+void dontwarn8(char *p) {
+  char *x;
+  x = (p ? p : "");
+}
+
+char *dontwarn9(char *p) {
+  char *x;
+  return p ? p : "";
+}
+
+void dontwarn10(int *p) {
+  ++(*p);
+}
+
+char dontwarn11(int *p) {
+  return ++(*p);
+}
+
+char *dontwarn12(char *s) {
+  return s;
+}
+
+void dontwarn13(unsigned char *str, const unsigned int i) {
+    unsigned char *p;
+    for (p = str + i; *p; ) {}
+}
+
+void dontwarn14(int *buf) {
+  int i, *p;
+  for (i=0, p=buf; i<10; i++, p++) {
+    *p = 1;
+  }
+}
+
+void dontwarn15(int *p, int x) {
+    for (int *q = p+x-1; 0; q++);
+}
+
+void dontwarn16(char *p) {
+  strcpy1(p, "abc");
+}
+
+void dontwarn17(char *p) {
+  strcpy1(p+2, "abc");
+}
+
+
+char *dontwarn18(char *p) {
+  return strcpy1(p, "abc");
+}
+
+unsigned dontwarn19(char *buf) {
+  unsigned len = my_strlen(buf);
+  return len + my_strcpy(buf, "abc");
+}
+
+// Don't warn about nonconst function pointers that can be const.
+void functionpointer(double f(double), int x) {
+  f(x);
+}
+
+// Don't warn about nonconst record pointers that can be const.
+struct XY { int x; int y; };
+int recordpointer(struct XY *xy) {
+  return xy->x;
+}
Index: lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- lib/Serialization/ASTReaderDecl.cpp
+++ lib/Serialization/ASTReaderDecl.cpp
@@ -501,6 +501,8 @@
   D->setImplicit(Record[Idx++]);
   D->Used = Record[Idx++];
   D->setReferenced(Record[Idx++]);
+  if (auto *VD = dyn_cast<VarDecl>(D))
+    VD->setNonConstUse();
   D->setTopLevelDeclInObjCContainer(Record[Idx++]);
   D->setAccess((AccessSpecifier)Record[Idx++]);
   D->FromASTFile = true;
Index: lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3632,6 +3632,7 @@
     if (OldVar->isUsed(false))
       NewVar->setIsUsed();
     NewVar->setReferenced(OldVar->isReferenced());
+    NewVar->setNonConstUse();
   }
 
   InstantiateAttrs(TemplateArgs, OldVar, NewVar, LateAttrs, StartingScope);
Index: lib/Sema/SemaOpenMP.cpp
===================================================================
--- lib/Sema/SemaOpenMP.cpp
+++ lib/Sema/SemaOpenMP.cpp
@@ -499,6 +499,7 @@
                                      SourceLocation Loc,
                                      bool RefersToCapture = false) {
   D->setReferenced();
+  D->setNonConstUse();
   D->markUsed(S.Context);
   return DeclRefExpr::Create(S.getASTContext(), NestedNameSpecifierLoc(),
                              SourceLocation(), D, RefersToCapture, Loc, Ty,
Index: lib/Sema/SemaLambda.cpp
===================================================================
--- lib/Sema/SemaLambda.cpp
+++ lib/Sema/SemaLambda.cpp
@@ -815,7 +815,8 @@
   VarDecl *NewVD = VarDecl::Create(Context, CurContext, Loc,
                                    Loc, Id, InitCaptureType, TSI, SC_Auto);
   NewVD->setInitCapture(true);
-  NewVD->setReferenced(true);
+  NewVD->setReferenced();
+  NewVD->setNonConstUse();
   NewVD->markUsed(Context);
   NewVD->setInit(Init);
   return NewVD;
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -9492,6 +9492,19 @@
   }
 }
 
+/// \brief Set NonConstUse for variable declarations used in expression.
+static void MarkNonConstUse(Expr *E) {
+  E = E->IgnoreParenCasts();
+  if (auto *D = dyn_cast<DeclRefExpr>(E)) {
+    if (auto *VD = dyn_cast<VarDecl>(D->getDecl()))
+      VD->setNonConstUse();
+  } else if (auto *U = dyn_cast<UnaryOperator>(E)) {
+    MarkNonConstUse(U->getSubExpr());
+  } else if (auto *A = dyn_cast<ArraySubscriptExpr>(E)) {
+    MarkNonConstUse(A->getBase());
+  }
+}
+
 // C99 6.5.16.1
 QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
                                        SourceLocation Loc,
@@ -9502,6 +9515,8 @@
   if (CheckForModifiableLvalue(LHSExpr, Loc, *this))
     return QualType();
 
+  MarkNonConstUse(LHSExpr);
+
   QualType LHSType = LHSExpr->getType();
   QualType RHSType = CompoundType.isNull() ? RHS.get()->getType() :
                                              CompoundType;
@@ -12807,7 +12822,8 @@
  
     CopyExpr = new (S.Context) DeclRefExpr(Var, RefersToCapturedVariable,
                                             DeclRefType, VK_LValue, Loc);
-    Var->setReferenced(true);
+    Var->setReferenced();
+    Var->setNonConstUse();
     Var->markUsed(S.Context);
   }
 
Index: lib/Sema/SemaDecl.cpp
===================================================================
--- lib/Sema/SemaDecl.cpp
+++ lib/Sema/SemaDecl.cpp
@@ -8975,6 +8975,24 @@
   }
 }
 
+static void MarkNonConstUse(Expr *E) {
+  E = E->IgnoreParenCasts();
+  if (auto *B = dyn_cast<BinaryOperator>(E)) {
+    if (B->isAdditiveOp()) {
+      MarkNonConstUse(B->getLHS());
+      MarkNonConstUse(B->getRHS());
+    }
+  } else if (auto *C = dyn_cast<ConditionalOperator>(E)) {
+    MarkNonConstUse(C->getFalseExpr());
+    MarkNonConstUse(C->getTrueExpr());
+  } else if (auto *D = dyn_cast<DeclRefExpr>(E)) {
+    if (auto *VD = dyn_cast<VarDecl>(D->getDecl()))
+      VD->setNonConstUse();
+  } else if (auto *U = dyn_cast<UnaryOperator>(E)) {
+    MarkNonConstUse(U->getSubExpr());
+  }
+}
+
 /// AddInitializerToDecl - Adds the initializer Init to the
 /// declaration dcl. If DirectInit is true, this is C++ direct
 /// initialization rather than copy initialization.
@@ -8987,6 +9005,8 @@
     return;
   }
 
+  MarkNonConstUse(Init);
+
   if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(RealDecl)) {
     // Pure-specifiers are handled in ActOnPureSpecifier.
     Diag(Method->getLocation(), diag::err_member_function_initialization)
@@ -10398,6 +10418,41 @@
   }
 }
 
+void Sema::DiagnoseNonConstPointerParameters(ParmVarDecl * const *ParamBegin,
+                                             ParmVarDecl * const *ParamEnd) {
+  // Don't diagnose nonconst-parameter errors in template instantiations
+  if (!ActiveTemplateInstantiations.empty())
+    return;
+  for (const auto *Param : llvm::make_range(ParamBegin, ParamEnd)) {
+    if (!Param->isReferenced() || Param->hasNonConstUse())
+      continue;
+    const Type *T = Param->getType().getTypePtr();
+    if (!T->isPointerType())
+      continue;
+    if (T->getPointeeType().isConstQualified())
+      continue;
+    // Don't warn about function pointers.
+    if (T->getPointeeType().getTypePtr()->isFunctionProtoType())
+      continue;
+    // Don't warn about struct parameters. These are not handled properly by
+    // the code currently. There should not be warnings always when a struct
+    // pointer parameter can be const neither.
+    if (T->getPointeeType().getTypePtr()->isRecordType())
+      continue;
+    // Don't warn about ** parameters. These are not handled properly by the
+    // code currently.
+    if (T->getPointeeType().getTypePtr()->isPointerType())
+      continue;
+    // Don't warn about void * as such parameters are often used in casts that
+    // are hard to handle well. For now there are no warnings about such
+    // parameters.
+    if (T->getPointeeType().getTypePtr()->isVoidType())
+      continue;
+    Diag(Param->getLocation(), diag::warn_nonconst_parameter)
+        << Param->getDeclName();
+  }
+}
+
 void Sema::DiagnoseSizeOfParametersAndReturnValue(ParmVarDecl * const *Param,
                                                   ParmVarDecl * const *ParamEnd,
                                                   QualType ReturnTy,
@@ -10970,15 +11025,17 @@
       // Don't diagnose unused parameters of defaulted or deleted functions.
       if (!FD->isDeleted() && !FD->isDefaulted())
         DiagnoseUnusedParameters(FD->param_begin(), FD->param_end());
+      if (!getLangOpts().CPlusPlus)
+        DiagnoseNonConstPointerParameters(FD->param_begin(), FD->param_end());
       DiagnoseSizeOfParametersAndReturnValue(FD->param_begin(), FD->param_end(),
                                              FD->getReturnType(), FD);
 
       // If this is a structor, we need a vtable.
       if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(FD))
         MarkVTableUsed(FD->getLocation(), Constructor->getParent());
       else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(FD))
         MarkVTableUsed(FD->getLocation(), Destructor->getParent());
-      
+
       // Try to apply the named return value optimization. We have to check
       // if we can do this here because lambdas keep return statements around
       // to deduce an implicit return type.
Index: lib/Parse/ParseStmt.cpp
===================================================================
--- lib/Parse/ParseStmt.cpp
+++ lib/Parse/ParseStmt.cpp
@@ -372,6 +372,87 @@
   return Res;
 }
 
+// Mark symbols in r-value expression as written.
+void Parser::MarkNonConstUse(Expr *E) {
+  E = E->IgnoreParenCasts();
+  if (auto *B = dyn_cast<BinaryOperator>(E)) {
+    if (B->isAdditiveOp()) {
+      // p + 2
+      MarkNonConstUse(B->getLHS());
+      MarkNonConstUse(B->getRHS());
+    } else if (B->isAssignmentOp()) {
+      // .. = p;
+      MarkNonConstUse(B->getRHS());
+    }
+  } else if (auto *CE = dyn_cast<CallExpr>(E)) {
+    // Mark nonconst function parameters as written.
+    const FunctionDecl *FD = CE->getDirectCallee();
+    if (!FD) {
+      // Mark all arguments as written
+      // TODO: Handle CXXMemberCallExpr better
+      for (auto *Arg : CE->arguments()) {
+        MarkNonConstUse(Arg);
+      }
+      return;
+    }
+    unsigned ArgNr = 0U;
+    for (const auto *Par : FD->parameters()) {
+      if (ArgNr >= CE->getNumArgs())
+        break;
+      // Is this a nonconstant pointer/reference parameter?
+      const Type *ParType = Par->getType().getTypePtr();
+      if ((ParType->isPointerType() && !ParType->getPointeeType().isConstQualified()) ||
+          (ParType->isReferenceType() && !Par->getType().isConstQualified())) {
+        // This is a nonconst pointer/reference parameter, the data is
+        // potentially written in the function.
+        Expr *Arg = CE->getArg(ArgNr);
+        if (ParType->isReferenceType())
+          if (auto *U = dyn_cast<UnaryOperator>(Arg))
+            Arg = U->getSubExpr();
+        MarkNonConstUse(Arg);
+      }
+      ArgNr++;
+    }
+  } else if (auto *C = dyn_cast<ConditionalOperator>(E)) {
+    MarkNonConstUse(C->getTrueExpr());
+    MarkNonConstUse(C->getFalseExpr());
+  } else if (auto *D = dyn_cast<DeclRefExpr>(E)) {
+    if (auto *VD = dyn_cast<VarDecl>(D->getDecl()))
+      VD->setNonConstUse();
+  } else if (isa<UnaryOperator>(E)) {
+    // ++(*p);
+    bool IncDec = false;
+    bool Deref = false;
+    while (auto *U = dyn_cast<UnaryOperator>(E)) {
+      switch (U->getOpcode()) {
+      case UO_PreInc:
+      case UO_PostInc:
+      case UO_PreDec:
+      case UO_PostDec:
+        IncDec = true;
+        break;
+      case UO_Deref:
+        if (IncDec)
+          Deref = true;
+        break;
+      default:
+        break;
+      };
+      E = U->getSubExpr()->IgnoreParenCasts();
+    }
+    if (auto *A = dyn_cast<ArraySubscriptExpr>(E)) {
+      Deref = IncDec;
+      E = A->getBase()->IgnoreParenCasts();
+    }
+    if (Deref) {
+      if (auto *D = dyn_cast<DeclRefExpr>(E)) {
+        if (auto *VD = dyn_cast<VarDecl>(D->getDecl()))
+          VD->setNonConstUse();
+      }
+    }
+  }
+}
+
 /// \brief Parse an expression statement.
 StmtResult Parser::ParseExprStatement() {
   // If a case keyword is missing, this is where it should be inserted.
@@ -389,6 +470,9 @@
     return Actions.ActOnExprStmtError();
   }
 
+  // Mark symbols in the expression as written.
+  MarkNonConstUse(Expr.get());
+
   if (Tok.is(tok::colon) && getCurScope()->isSwitchScope() &&
       Actions.CheckCaseExpression(Expr.get())) {
     // If a constant expression is followed by a colon inside a switch block,
@@ -954,6 +1038,18 @@
     StmtResult R;
     if (Tok.isNot(tok::kw___extension__)) {
       R = ParseStatementOrDeclaration(Stmts, false);
+      if (!R.isInvalid() && R.get()) {
+        if (ReturnStmt *RS = dyn_cast<ReturnStmt>(R.get())) {
+          if (RS->getRetValue()) {
+            if (auto *Cast = dyn_cast<ImplicitCastExpr>(RS->getRetValue())) {
+              const Type *T = Cast->getType().getTypePtr();
+              if (T->isPointerType() && !T->getPointeeType().isConstQualified()) {
+                MarkNonConstUse(Cast);
+              }
+            }
+          }
+        }
+      }
     } else {
       // __extension__ can start declarations and it can also be a unary
       // operator for expressions.  Consume multiple __extension__ markers here
Index: lib/Parse/ParseExpr.cpp
===================================================================
--- lib/Parse/ParseExpr.cpp
+++ lib/Parse/ParseExpr.cpp
@@ -167,7 +167,22 @@
   ExprResult LHS = ParseCastExpression(/*isUnaryExpression=*/false,
                                        /*isAddressOfOperand=*/false,
                                        isTypeCast);
-  return ParseRHSOfBinaryExpression(LHS, prec::Assignment);
+
+  ExprResult ER(ParseRHSOfBinaryExpression(LHS, prec::Assignment));
+  if (ER.isInvalid())
+    return ER;
+
+  if (auto *B = dyn_cast<BinaryOperator>(ER.get())) {
+    if (B->isAssignmentOp() || B->isAdditiveOp()) {
+      MarkNonConstUse(B->getLHS());
+      MarkNonConstUse(B->getRHS());
+    }
+  } else if (isa<CallExpr>(ER.get()) ||
+             isa<ConditionalOperator>(ER.get()) ||
+             isa<UnaryOperator>(ER.get())) {
+    MarkNonConstUse(ER.get());
+  }
+  return ER;
 }
 
 /// \brief Parse an assignment expression where part of an Objective-C message
@@ -408,6 +423,9 @@
         if (TernaryMiddle.isUsable())
           TernaryMiddle = Actions.CorrectDelayedTyposInExpr(TernaryMiddle);
         LHS = ExprError();
+      } else if (auto *B = dyn_cast<BinaryOperator>(RHS.get())) {
+        if (B->isAssignmentOp())
+          MarkNonConstUse(B->getRHS());
       }
 
       NextTokPrec = getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator,
Index: lib/AST/Decl.cpp
===================================================================
--- lib/AST/Decl.cpp
+++ lib/AST/Decl.cpp
@@ -1762,7 +1762,7 @@
                  IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo,
                  StorageClass SC)
     : DeclaratorDecl(DK, DC, IdLoc, Id, T, TInfo, StartLoc),
-      redeclarable_base(C), Init() {
+      redeclarable_base(C), Init(), NonConstUse(false) {
   static_assert(sizeof(VarDeclBitfields) <= sizeof(unsigned),
                 "VarDeclBitfields too large!");
   static_assert(sizeof(ParmVarDeclBitfields) <= sizeof(unsigned),
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -1762,6 +1762,11 @@
   void DiagnoseUnusedParameters(ParmVarDecl * const *Begin,
                                 ParmVarDecl * const *End);
 
+  /// \brief Diagnose pointer parameters that should be const in the given
+  /// sequence of ParmVarDecl pointers.
+  void DiagnoseNonConstPointerParameters(ParmVarDecl * const *Begin,
+                                         ParmVarDecl * const *End);
+
   /// \brief Diagnose whether the size of parameters or return value of a
   /// function or obj-c method definition is pass-by-value and larger than a
   /// specified threshold.
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -1395,6 +1395,10 @@
                                   bool IsUnevaluated);
 
 private:
+  /// \brief Mark variable declarations in expression as nonconstuse, to prevent
+  /// warnings that they can be const.
+  static void MarkNonConstUse(Expr *E);
+
   ExprResult ParseExpressionWithLeadingAt(SourceLocation AtLoc);
 
   ExprResult ParseExpressionWithLeadingExtension(SourceLocation ExtLoc);
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -197,6 +197,8 @@
 def err_parameter_name_omitted : Error<"parameter name omitted">;
 def warn_unused_parameter : Warning<"unused parameter %0">,
   InGroup<UnusedParameter>, DefaultIgnore;
+def warn_nonconst_parameter : Warning<"parameter %0 can be const">,
+  InGroup<NonConstParameter>, DefaultIgnore;
 def warn_unused_variable : Warning<"unused variable %0">,
   InGroup<UnusedVariable>, DefaultIgnore;
 def warn_unused_local_typedef : Warning<
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td
+++ include/clang/Basic/DiagnosticGroups.td
@@ -433,6 +433,7 @@
 def UnusedLabel : DiagGroup<"unused-label">;
 def UnusedParameter : DiagGroup<"unused-parameter">;
 def UnusedResult : DiagGroup<"unused-result">;
+def NonConstParameter : DiagGroup<"nonconst-parameter">;
 def PotentiallyEvaluatedExpression : DiagGroup<"potentially-evaluated-expression">;
 def UnevaluatedExpression : DiagGroup<"unevaluated-expression",
                                       [PotentiallyEvaluatedExpression]>;
Index: include/clang/AST/Decl.h
===================================================================
--- include/clang/AST/Decl.h
+++ include/clang/AST/Decl.h
@@ -850,6 +850,9 @@
     return getMostRecentDecl();
   }
 
+  /// \brief Whether this variable has non-const use so it can't be const.
+  unsigned NonConstUse:1;
+
 public:
   typedef redeclarable_base::redecl_range redecl_range;
   typedef redeclarable_base::redecl_iterator redecl_iterator;
@@ -865,6 +868,13 @@
                          IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo,
                          StorageClass S);
 
+  /// \brief Whether the declared symbol has non-const usage so it can't be
+  /// const.
+  bool hasNonConstUse() const { return NonConstUse; }
+
+  /// \brief Use this method when this symbol can't be const.
+  void setNonConstUse() { NonConstUse = isThisDeclarationReferenced(); }
+
   static VarDecl *CreateDeserialized(ASTContext &C, unsigned ID);
 
   SourceRange getSourceRange() const override LLVM_READONLY;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to