nickdesaulniers created this revision. nickdesaulniers added reviewers: rsmith, aaron.ballman, craig.topper, efriedma, lebedev.ri, jdoerfert, arsenm. Herald added subscribers: dexonsmith, hiraditya. nickdesaulniers requested review of this revision. Herald added subscribers: llvm-commits, cfe-commits, wdng. Herald added projects: clang, LLVM.
Add support for the GNU C style __attribute__((error(""))) and __attribute__((warning(""))). These attributes are meant to be put on definitions of functions whom should not be called. They are frequently used to provide compile time diagnostics similar to _Static_assert, but which may rely on non-ICE conditions (ie. relying on compiler optimizations). While users may instead simply call undefined functions in such cases to get a linkage failure from the linker, these provide a much more ergonomic and actionable diagnostic to users and do so at compile time rather than at link time. These are used throughout the Linux kernel in its implementation of BUILD_BUG and BUILD_BUG_ON macros. These macros generally cannot be converted to use _Static_assert because many of the parameters are not ICEs. The Linux kernel still needs to be modified to make use of these when building with Clang; I have a patch that does so I will send once this feature is landed. To do so, we create a new IR level Function attribute, "user-diagnostic"="" which contains the string parameter from source language function attributes (both error and warning boil down to one IR Fn Attr). Then, similar to calls to inline asm, we attach a !srcloc Metadata node to call sites of such attributed callees. The backend diagnoses these before instruction selection, while we still know that a call is a call (vs say a JMP that's a tail call) in an arch agnostic manner. The frontend then reconstructs the SourceLocation from that Metadata, and determines whether to emit an error or warning based on the callee's attribute. Some things to iron out TODO: - Is user-diagnostic the best identifier for the new fn attr? Maybe "diagnose-if-called"? I also use `UserDiagnostic` throughout much of this patch for C++ class names; surely there's something better? - When is the best time for the backend to produce such diagnostics? If we wait until post-ISEL, then we need to diagnose instructions which may be JMPs, not just CALLs. Link: https://bugs.llvm.org/show_bug.cgi?id=16428 Link: https://github.com/ClangBuiltLinux/linux/issues/1173 Repository: rG LLVM Github Monorepo 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,32 @@ 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>()) { + llvm::errs() << "should be a warning diag\n"; + 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 +860,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