nickdesaulniers updated this revision to Diff 358814.
nickdesaulniers added a comment.

- remove accidentally committed logging statement


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D106030

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DiagnosticFrontendKinds.td
  clang/include/clang/Basic/DiagnosticGroups.td
  clang/lib/CodeGen/CGCall.cpp
  clang/lib/CodeGen/CodeGenAction.cpp
  clang/lib/CodeGen/CodeGenModule.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/test/CodeGen/attr-error.c
  clang/test/CodeGen/attr-warning.c
  clang/test/Frontend/backend-attribute-error-warning-optimize.c
  clang/test/Frontend/backend-attribute-error-warning.c
  clang/test/Frontend/backend-attribute-error-warning.cpp
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/Sema/attr-error.c
  clang/test/Sema/attr-warning.c
  llvm/docs/LangRef.rst
  llvm/include/llvm/IR/DiagnosticInfo.h
  llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
  llvm/lib/IR/DiagnosticInfo.cpp
  llvm/test/CodeGen/Generic/attr-user-diagnostic.ll

Index: llvm/test/CodeGen/Generic/attr-user-diagnostic.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/Generic/attr-user-diagnostic.ll
@@ -0,0 +1,9 @@
+; RUN: not llc -stop-after=pre-isel-intrinsic-lowering %s 2>&1 | FileCheck %s
+
+declare void @foo() "user-diagnostic"="oh no"
+define void @bar() {
+  call void @foo()
+  ret void
+}
+
+; CHECK: error: call to foo: oh no
Index: llvm/lib/IR/DiagnosticInfo.cpp
===================================================================
--- llvm/lib/IR/DiagnosticInfo.cpp
+++ llvm/lib/IR/DiagnosticInfo.cpp
@@ -401,3 +401,7 @@
 
 void OptimizationRemarkAnalysisFPCommute::anchor() {}
 void OptimizationRemarkAnalysisAliasing::anchor() {}
+
+void DiagnosticInfoUser::print(DiagnosticPrinter &DP) const {
+  DP << "call to " << getFunctionName() << ": " << getMsgStr();
+}
Index: llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
===================================================================
--- llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
+++ llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
@@ -14,6 +14,7 @@
 #include "llvm/CodeGen/PreISelIntrinsicLowering.h"
 #include "llvm/Analysis/ObjCARCInstKind.h"
 #include "llvm/CodeGen/Passes.h"
+#include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/Instructions.h"
@@ -204,6 +205,25 @@
       Changed |= lowerObjCCall(F, "objc_sync_exit");
       break;
     }
+    // TODO: is this the best pass for this?
+    for (BasicBlock &BB : F) {
+      for (Instruction &I : BB) {
+        if (CallBase *CB = dyn_cast<CallBase>(&I)) {
+          if (Function *Callee = CB->getCalledFunction()) {
+            if (Callee->hasFnAttribute("user-diagnostic")) {
+              unsigned LocCookie = 0;
+              if (MDNode *MD = CB->getMetadata("srcloc"))
+                LocCookie = mdconst::extract<ConstantInt>(MD->getOperand(0))
+                                ->getZExtValue();
+              DiagnosticInfoUser DU(
+                  Callee->getFnAttribute("user-diagnostic").getValueAsString(),
+                  Callee->getName(), LocCookie);
+              F.getContext().diagnose(DU);
+            }
+          }
+        }
+      }
+    }
   }
   return Changed;
 }
Index: llvm/include/llvm/IR/DiagnosticInfo.h
===================================================================
--- llvm/include/llvm/IR/DiagnosticInfo.h
+++ llvm/include/llvm/IR/DiagnosticInfo.h
@@ -79,6 +79,7 @@
   DK_PGOProfile,
   DK_Unsupported,
   DK_SrcMgr,
+  DK_UserDiagnostic,
   DK_FirstPluginKind // Must be last value to work with
                      // getNextAvailablePluginDiagnosticKind
 };
@@ -1070,6 +1071,25 @@
   }
 };
 
+class DiagnosticInfoUser : public DiagnosticInfo {
+  StringRef MsgStr;
+  StringRef CalleeName;
+  unsigned LocCookie;
+
+public:
+  DiagnosticInfoUser(StringRef MsgStr, StringRef CalleeName, unsigned LocCookie,
+                     DiagnosticSeverity Severity = DS_Error)
+      : DiagnosticInfo(DK_UserDiagnostic, Severity), MsgStr(MsgStr),
+        CalleeName(CalleeName), LocCookie(LocCookie) {}
+  StringRef getMsgStr() const { return MsgStr; }
+  StringRef getFunctionName() const { return CalleeName; }
+  unsigned getLocCookie() const { return LocCookie; }
+  void print(DiagnosticPrinter &DP) const override;
+  static bool classof(const DiagnosticInfo *DI) {
+    return DI->getKind() == DK_UserDiagnostic;
+  }
+};
+
 } // end namespace llvm
 
 #endif // LLVM_IR_DIAGNOSTICINFO_H
Index: llvm/docs/LangRef.rst
===================================================================
--- llvm/docs/LangRef.rst
+++ llvm/docs/LangRef.rst
@@ -2037,6 +2037,12 @@
     show that no exceptions passes by it. This is normally the case for
     the ELF x86-64 abi, but it can be disabled for some compilation
     units.
+``"user-diagnostic"="<diagnostic string>"``
+    This attribute provides a diagnostic string to emit when a call of a
+    function with this attribute is not eliminated via optimization. Front ends
+    can provide optional ``srcloc`` metadata nodes on call sites of such
+    callees to attach information about where in the source language such a
+    call came from.
 ``nocf_check``
     This attribute indicates that no control-flow check will be performed on
     the attributed entity. It disables -fcf-protection=<> for a specific
Index: clang/test/Sema/attr-warning.c
===================================================================
--- /dev/null
+++ clang/test/Sema/attr-warning.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+#if !__has_attribute(warning)
+#warning "warning attribute missing"
+#endif
+
+__attribute__((warning("don't call me!"))) int foo(void);
+
+__attribute__((warning)) // expected-error {{'warning' attribute takes one argument}}
+int
+bad0(void);
+
+int bad1(__attribute__((warning("bad1"))) int param); // expected-warning {{'warning' attribute only applies to functions}}
+
+int bad2(void) {
+  __attribute__((warning("bad2"))); // expected-error {{'warning' attribute cannot be applied to a statement}}
+}
+
+__attribute__((warning(3))) // expected-error {{'warning' attribute requires a string}}
+int
+bad3(void);
Index: clang/test/Sema/attr-error.c
===================================================================
--- /dev/null
+++ clang/test/Sema/attr-error.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+#if !__has_attribute(error)
+#error "error attribute missing"
+#endif
+
+__attribute__((error("don't call me!"))) int foo(void);
+
+__attribute__((error)) // expected-error {{'error' attribute takes one argument}}
+int
+bad0(void);
+
+int bad1(__attribute__((error("bad1"))) int param); // expected-error {{'error' attribute only applies to functions}}
+
+int bad2(void) {
+  __attribute__((error("bad2"))); // expected-error {{'error' attribute cannot be applied to a statement}}
+}
+
+__attribute__((error(3))) // expected-error {{'error' attribute requires a string}}
+int
+bad3(void);
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -61,6 +61,7 @@
 // CHECK-NEXT: EnforceTCB (SubjectMatchRule_function)
 // CHECK-NEXT: EnforceTCBLeaf (SubjectMatchRule_function)
 // CHECK-NEXT: EnumExtensibility (SubjectMatchRule_enum)
+// CHECK-NEXT: Error (SubjectMatchRule_function)
 // CHECK-NEXT: ExcludeFromExplicitInstantiation (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
 // CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
 // CHECK-NEXT: FlagEnum (SubjectMatchRule_enum)
@@ -177,6 +178,7 @@
 // CHECK-NEXT: VecTypeHint (SubjectMatchRule_function)
 // CHECK-NEXT: WarnUnused (SubjectMatchRule_record)
 // CHECK-NEXT: WarnUnusedResult (SubjectMatchRule_objc_method, SubjectMatchRule_enum, SubjectMatchRule_record, SubjectMatchRule_hasType_functionType)
+// CHECK-NEXT: Warning (SubjectMatchRule_function)
 // CHECK-NEXT: Weak (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
 // CHECK-NEXT: WeakRef (SubjectMatchRule_variable, SubjectMatchRule_function)
 // CHECK-NEXT: WebAssemblyExportName (SubjectMatchRule_function)
Index: clang/test/Frontend/backend-attribute-error-warning.cpp
===================================================================
--- /dev/null
+++ clang/test/Frontend/backend-attribute-error-warning.cpp
@@ -0,0 +1,29 @@
+// REQUIRES: x86-registered-target
+// RUN: %clang_cc1 -verify -emit-codegen-only %s
+
+__attribute__((error("oh no foo"))) void foo(void);
+
+__attribute__((error("oh no bar"))) void bar(void);
+
+int x(void) {
+  return 8 % 2 == 1;
+}
+
+__attribute__((warning("oh no quux"))) void quux(void);
+
+__attribute__((error("demangle me"))) void __compiletime_assert_455(void);
+
+template <typename T>
+__attribute__((error("demangle me, too")))
+T
+nocall(T t);
+
+void baz(void) {
+  // Test that we demangle correctly in the diagnostic for C++.
+  foo(); // expected-error {{call to foo declared with attribute error: oh no foo}}
+  if (x())
+    bar();                    // expected-error {{call to bar declared with attribute error: oh no bar}}
+  quux();                     // expected-warning {{call to quux declared with attribute warning: oh no quux}}
+  __compiletime_assert_455(); // expected-error {{call to __compiletime_assert_455 declared with attribute error: demangle me}}
+  nocall<int>(42);            // expected-error {{call to nocall declared with attribute error: demangle me, too}}
+}
Index: clang/test/Frontend/backend-attribute-error-warning.c
===================================================================
--- /dev/null
+++ clang/test/Frontend/backend-attribute-error-warning.c
@@ -0,0 +1,22 @@
+// REQUIRES: x86-registered-target
+// RUN: %clang_cc1 -verify -emit-codegen-only %s
+
+__attribute__((error("oh no foo"))) void foo(void);
+
+__attribute__((error("oh no bar"))) void bar(void);
+
+int x(void) {
+  return 8 % 2 == 1;
+}
+
+__attribute__((warning("oh no quux"))) void quux(void);
+
+__attribute__((error("demangle me"))) void __compiletime_assert_455(void);
+
+void baz(void) {
+  foo(); // expected-error {{call to foo declared with attribute error: oh no foo}}
+  if (x())
+    bar();                    // expected-error {{call to bar declared with attribute error: oh no bar}}
+  quux();                     // expected-warning {{call to quux declared with attribute warning: oh no quux}}
+  __compiletime_assert_455(); // expected-error {{call to __compiletime_assert_455 declared with attribute error: demangle me}}
+}
Index: clang/test/Frontend/backend-attribute-error-warning-optimize.c
===================================================================
--- /dev/null
+++ clang/test/Frontend/backend-attribute-error-warning-optimize.c
@@ -0,0 +1,15 @@
+// REQUIRES: x86-registered-target
+// RUN: %clang_cc1 -O2 -verify -emit-codegen-only %s
+
+__attribute__((error("oh no foo"))) void foo(void);
+
+__attribute__((error("oh no bar"))) void bar(void);
+
+int x(void) {
+  return 8 % 2 == 1;
+}
+void baz(void) {
+  foo(); // expected-error {{call to foo declared with attribute error: oh no foo}}
+  if (x())
+    bar();
+}
Index: clang/test/CodeGen/attr-warning.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/attr-warning.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+__attribute__((warning("oh no"))) void foo(void);
+
+void bar(void) {
+  foo();
+}
+
+// CHECK: call void @foo(), !srcloc [[SRCLOC:![0-9]+]]
+// CHECK: declare void @foo() [[ATTR:#[0-9]+]]
+// CHECK: attributes [[ATTR]] = {{{.*}}"user-diagnostic"="oh no"
+// CHECK: [[SRCLOC]] = !{i32 {{[0-9]+}}}
Index: clang/test/CodeGen/attr-error.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/attr-error.c
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+__attribute__((error("oh no"))) void foo(void);
+
+void bar(void) {
+  foo();
+}
+
+// CHECK: call void @foo(), !srcloc [[SRCLOC:![0-9]+]]
+// CHECK: declare void @foo() [[ATTR:#[0-9]+]]
+// CHECK: attributes [[ATTR]] = {{{.*}}"user-diagnostic"="oh no"
+// CHECK: [[SRCLOC]] = !{i32 {{[0-9]+}}}
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -946,6 +946,19 @@
     D->addAttr(::new (S.Context) EnableIfAttr(S.Context, AL, Cond, Msg));
 }
 
+static void handleErrorAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  StringRef Str;
+  if (!S.checkStringLiteralArgumentAttr(AL, 0, Str))
+    return;
+  D->addAttr(::new (S.Context) ErrorAttr(S.Context, AL, Str));
+}
+static void handleWarningAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  StringRef Str;
+  if (!S.checkStringLiteralArgumentAttr(AL, 0, Str))
+    return;
+  D->addAttr(::new (S.Context) WarningAttr(S.Context, AL, Str));
+}
+
 namespace {
 /// Determines if a given Expr references any of the given function's
 /// ParmVarDecls, or the function's implicit `this` parameter (if applicable).
@@ -7843,6 +7856,9 @@
   case ParsedAttr::AT_EnableIf:
     handleEnableIfAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_Error:
+    handleErrorAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_DiagnoseIf:
     handleDiagnoseIfAttr(S, D, AL);
     break;
@@ -8058,6 +8074,9 @@
   case ParsedAttr::AT_TypeVisibility:
     handleVisibilityAttr(S, D, AL, true);
     break;
+  case ParsedAttr::AT_Warning:
+    handleWarningAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_WarnUnusedResult:
     handleWarnUnusedResult(S, D, AL);
     break;
Index: clang/lib/CodeGen/CodeGenModule.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenModule.cpp
+++ clang/lib/CodeGen/CodeGenModule.cpp
@@ -2172,6 +2172,13 @@
                                                CalleeIdx, PayloadIndices,
                                                /* VarArgsArePassed */ false)}));
   }
+
+  // TODO: move to CodeGenModule::ConstructAttributeList() or
+  // CodeGenFunction::EmitCall() ???
+  if (const auto *EA = FD->getAttr<ErrorAttr>())
+    F->addFnAttr("user-diagnostic", EA->getUserDiagnostic());
+  if (const auto *WA = FD->getAttr<WarningAttr>())
+    F->addFnAttr("user-diagnostic", WA->getUserDiagnostic());
 }
 
 void CodeGenModule::addUsedGlobal(llvm::GlobalValue *GV) {
Index: clang/lib/CodeGen/CodeGenAction.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenAction.cpp
+++ clang/lib/CodeGen/CodeGenAction.cpp
@@ -401,6 +401,7 @@
         const llvm::OptimizationRemarkAnalysisAliasing &D);
     void OptimizationFailureHandler(
         const llvm::DiagnosticInfoOptimizationFailure &D);
+    void UserDiagnosticHandler(const DiagnosticInfoUser &D);
   };
 
   void BackendConsumer::anchor() {}
@@ -758,6 +759,30 @@
   EmitOptimizationMessage(D, diag::warn_fe_backend_optimization_failure);
 }
 
+void BackendConsumer::UserDiagnosticHandler(const DiagnosticInfoUser &D) {
+  unsigned DiagID = diag::err_fe_backend_user_diagnostic;
+  llvm::StringRef CalleeName = D.getFunctionName();
+  SourceLocation LocCookie =
+      SourceLocation::getFromRawEncoding(D.getLocCookie());
+
+  if (const Decl *D = Gen->GetDeclForMangledName(CalleeName)) {
+    if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+      assert((FD->hasAttr<ErrorAttr>() || FD->hasAttr<WarningAttr>()) &&
+             "expected error or warning function attribute");
+
+      if (FD->hasAttr<WarningAttr>())
+        DiagID = diag::warn_fe_backend_user_diagnostic;
+
+      CalleeName = FD->getName();
+    }
+  }
+
+  if (LocCookie.isValid())
+    Diags.Report(LocCookie, DiagID) << CalleeName << D.getMsgStr();
+  else
+    Diags.Report(DiagID) << CalleeName << D.getMsgStr();
+}
+
 /// This function is invoked when the backend needs
 /// to report something to the user.
 void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) {
@@ -833,6 +858,9 @@
   case llvm::DK_Unsupported:
     UnsupportedDiagHandler(cast<DiagnosticInfoUnsupported>(DI));
     return;
+  case llvm::DK_UserDiagnostic:
+    UserDiagnosticHandler(cast<DiagnosticInfoUser>(DI));
+    return;
   default:
     // Plugin IDs are not bound to any value as they are set dynamically.
     ComputeDiagRemarkID(Severity, backend_plugin, DiagID);
Index: clang/lib/CodeGen/CGCall.cpp
===================================================================
--- clang/lib/CodeGen/CGCall.cpp
+++ clang/lib/CodeGen/CGCall.cpp
@@ -5304,6 +5304,17 @@
       TargetDecl->hasAttr<MSAllocatorAttr>())
     getDebugInfo()->addHeapAllocSiteMetadata(CI, RetTy->getPointeeType(), Loc);
 
+  // Add metadata if calling an __attribute__((error(""))) or warning fn.
+  if (TargetDecl)
+    if (TargetDecl->hasAttr<ErrorAttr>() ||
+        TargetDecl->hasAttr<WarningAttr>()) {
+      llvm::ConstantInt *Line =
+          llvm::ConstantInt::get(Int32Ty, Loc.getRawEncoding());
+      llvm::ConstantAsMetadata *MD = llvm::ConstantAsMetadata::get(Line);
+      llvm::MDTuple *MDT = llvm::MDNode::get(getLLVMContext(), {MD});
+      CI->setMetadata("srcloc", MDT);
+    }
+
   // 4. Finish the call.
 
   // If the call doesn't return, finish the basic block and clear the
Index: clang/include/clang/Basic/DiagnosticGroups.td
===================================================================
--- clang/include/clang/Basic/DiagnosticGroups.td
+++ clang/include/clang/Basic/DiagnosticGroups.td
@@ -1202,6 +1202,7 @@
 def BackendOptimizationRemarkMissed : DiagGroup<"pass-missed">;
 def BackendOptimizationRemarkAnalysis : DiagGroup<"pass-analysis">;
 def BackendOptimizationFailure : DiagGroup<"pass-failed">;
+def BackendUserDiagnostic : DiagGroup<"user-diagnostic">;
 
 // Instrumentation based profiling warnings.
 def ProfileInstrMissing : DiagGroup<"profile-instr-missing">;
Index: clang/include/clang/Basic/DiagnosticFrontendKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -72,6 +72,11 @@
 def err_fe_backend_unsupported : Error<"%0">, BackendInfo;
 def warn_fe_backend_unsupported : Warning<"%0">, BackendInfo;
 
+def err_fe_backend_user_diagnostic :
+  Error<"call to %0 declared with attribute error: %1">, BackendInfo;
+def warn_fe_backend_user_diagnostic :
+  Warning<"call to %0 declared with attribute warning: %1">, BackendInfo, InGroup<BackendUserDiagnostic>;
+
 def err_fe_invalid_code_complete_file : Error<
     "cannot locate code-completion file %0">, DefaultFatal;
 def err_fe_dependency_file_requires_MT : Error<
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -6015,3 +6015,24 @@
   - ``enforce_tcb_leaf(Name)`` indicates that this function is a part of the TCB named ``Name``
   }];
 }
+
+def ErrorAttrDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+    The ``error`` function attribute can be used to specify a custom diagnostic
+    to be emitted when a call to such a function is not eliminated via
+    optimizations. This can be used to create compile time assertions that
+    depend on optimizations, while providing diagnostics pointing to precise
+    locations of the call site in the source.
+  }];
+}
+def WarningAttrDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+    The ``warning`` function attribute can be used to specify a custom
+    diagnostic to be emitted when a call to such a function is not eliminated
+    via optimizations. This can be used to create compile time assertions that
+    depend on optimizations, while providing diagnostics pointing to precise
+    locations of the call site in the source.
+  }];
+}
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -3812,3 +3812,17 @@
   let Documentation = [EnforceTCBLeafDocs];
   bit InheritEvenIfAlreadyPresent = 1;
 }
+
+def Error : Attr {
+  let Spellings = [GCC<"error">];
+  let Args = [StringArgument<"UserDiagnostic">];
+  let Subjects = SubjectList<[Function], ErrorDiag>;
+  let Documentation = [ErrorAttrDocs];
+}
+
+def Warning : Attr {
+  let Spellings = [GCC<"warning">];
+  let Args = [StringArgument<"UserDiagnostic">];
+  let Subjects = SubjectList<[Function]>;
+  let Documentation = [WarningAttrDocs];
+}
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -122,7 +122,8 @@
 C Language Changes in Clang
 ---------------------------
 
-- ...
+- Support for ``__attribute__((error("")))`` and
+  ``__attribute__((warning("")))`` function attributes have been added.
 
 C++ Language Changes in Clang
 -----------------------------
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to