Skip to content

[Clang] include attribute scope in diagnostics #144619

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions clang/include/clang/Basic/AttributeCommonInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#define LLVM_CLANG_BASIC_ATTRIBUTECOMMONINFO_H

#include "clang/Basic/AttributeScopeInfo.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/TokenKinds.h"

Expand Down Expand Up @@ -175,6 +176,10 @@ class AttributeCommonInfo {
: AttributeCommonInfo(nullptr, AttributeScopeInfo(), AttrRange, K,
FormUsed) {}

AttributeCommonInfo(SourceRange AttrRange, AttributeScopeInfo AttrScope,
Kind K, Form FormUsed)
: AttributeCommonInfo(nullptr, AttrScope, AttrRange, K, FormUsed) {}

AttributeCommonInfo(AttributeCommonInfo &&) = default;
AttributeCommonInfo(const AttributeCommonInfo &) = default;

Expand Down Expand Up @@ -292,6 +297,18 @@ inline bool doesKeywordAttributeTakeArgs(tok::TokenKind Kind) {
}
}

inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
const AttributeCommonInfo *CI) {
DB.AddTaggedVal(reinterpret_cast<uint64_t>(CI),
DiagnosticsEngine::ak_attr_info);
return DB;
}

inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
const AttributeCommonInfo &CI) {
return DB << &CI;
}

} // namespace clang

#endif // LLVM_CLANG_BASIC_ATTRIBUTECOMMONINFO_H
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/Diagnostic.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {

/// Expr *
ak_expr,

/// AttributeCommonInfo *
ak_attr_info,
};

/// Represents on argument value, which is a union discriminated
Expand Down
15 changes: 8 additions & 7 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -3290,10 +3290,11 @@ def err_attribute_wrong_number_arguments : Error<
def err_attribute_wrong_number_arguments_for : Error <
"%0 attribute references function %1, which %plural{0:takes no arguments|1:takes one argument|"
":takes exactly %2 arguments}2">;
def err_callback_attribute_wrong_arg_count : Error<
"'callback' attribute references function of type %0 which expects %1 "
"%plural{1:argument|:arguments}1 but attribute specifies %2 parameter index "
"%plural{1:argument|:arguments}2">;
def err_attribute_wrong_arg_count_for_func
: Error<"%0 attribute references function of type %1 which expects %2 "
"%plural{1:argument|:arguments}2 but attribute specifies %3 "
"parameter index "
"%plural{1:argument|:arguments}3">;
def err_attribute_bounds_for_function : Error<
"%0 attribute references parameter %1, but the function %2 has only %3 parameters">;
def err_attribute_no_member_function : Error<
Expand Down Expand Up @@ -4712,9 +4713,9 @@ def note_protocol_decl : Note<
"protocol is declared here">;
def note_protocol_decl_undefined : Note<
"protocol %0 has no definition">;
def err_attribute_preferred_name_arg_invalid : Error<
"argument %0 to 'preferred_name' attribute is not a typedef for "
"a specialization of %1">;
def err_attribute_not_typedef_for_specialization
: Error<"argument %0 to %1 attribute is not a typedef for "
"a specialization of %2">;
def err_attribute_builtin_alias : Error<
"%0 attribute can only be applied to a ARM, HLSL, SPIR-V or RISC-V builtin">;

Expand Down
39 changes: 0 additions & 39 deletions clang/include/clang/Sema/ParsedAttr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1094,45 +1094,6 @@ enum AttributeDeclKind {
ExpectedTypedef,
};

inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
const ParsedAttr &At) {
DB.AddTaggedVal(reinterpret_cast<uint64_t>(At.getAttrName()),
DiagnosticsEngine::ak_identifierinfo);
return DB;
}

inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
const ParsedAttr *At) {
DB.AddTaggedVal(reinterpret_cast<uint64_t>(At->getAttrName()),
DiagnosticsEngine::ak_identifierinfo);
return DB;
}

/// AttributeCommonInfo has a non-explicit constructor which takes an
/// SourceRange as its only argument, this constructor has many uses so making
/// it explicit is hard. This constructor causes ambiguity with
/// DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, SourceRange R).
/// We use SFINAE to disable any conversion and remove any ambiguity.
template <
typename ACI,
std::enable_if_t<std::is_same<ACI, AttributeCommonInfo>::value, int> = 0>
inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
const ACI &CI) {
DB.AddTaggedVal(reinterpret_cast<uint64_t>(CI.getAttrName()),
DiagnosticsEngine::ak_identifierinfo);
return DB;
}

template <
typename ACI,
std::enable_if_t<std::is_same<ACI, AttributeCommonInfo>::value, int> = 0>
inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
const ACI *CI) {
DB.AddTaggedVal(reinterpret_cast<uint64_t>(CI->getAttrName()),
DiagnosticsEngine::ak_identifierinfo);
return DB;
}

} // namespace clang

#endif // LLVM_CLANG_SEMA_PARSEDATTR_H
24 changes: 23 additions & 1 deletion clang/lib/AST/ASTDiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,15 @@ void clang::FormatASTNodeDiagnosticArgument(
case DiagnosticsEngine::ak_attr: {
const Attr *At = reinterpret_cast<Attr *>(Val);
assert(At && "Received null Attr object!");
OS << '\'' << At->getSpelling() << '\'';

OS << '\'';
if (At->hasScope()) {
OS << At->getNormalizedFullName(At->getScopeName()->getName(),
At->getSpelling());
} else {
OS << At->getSpelling();
}
OS << '\'';
NeedQuotes = false;
break;
}
Expand All @@ -516,6 +524,20 @@ void clang::FormatASTNodeDiagnosticArgument(
E->printPretty(OS, /*Helper=*/nullptr, Context.getPrintingPolicy());
break;
}
case DiagnosticsEngine::ak_attr_info: {
AttributeCommonInfo *AT = reinterpret_cast<AttributeCommonInfo *>(Val);
assert(AT && "Received null AttributeCommonInfo object!");

OS << '\'';
if (AT->isStandardAttributeSyntax()) {
OS << AT->getNormalizedFullName();
} else {
OS << AT->getAttrName()->getName();
}
OS << '\'';
NeedQuotes = false;
break;
}
}

if (NeedQuotes) {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/Diagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1348,6 +1348,7 @@ void Diagnostic::FormatDiagnostic(const char *DiagStr, const char *DiagEnd,
case DiagnosticsEngine::ak_declcontext:
case DiagnosticsEngine::ak_attr:
case DiagnosticsEngine::ak_expr:
case DiagnosticsEngine::ak_attr_info:
getDiags()->ConvertArgToString(Kind, getRawArg(ArgNo),
StringRef(Modifier, ModifierLen),
StringRef(Argument, ArgumentLen),
Expand Down
16 changes: 7 additions & 9 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1226,8 +1226,8 @@ static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) {
}
}

S.Diag(AL.getLoc(), diag::err_attribute_preferred_name_arg_invalid)
<< T << CTD;
S.Diag(AL.getLoc(), diag::err_attribute_not_typedef_for_specialization)
<< T << AL << CTD;
if (const auto *TT = T->getAs<TypedefType>())
S.Diag(TT->getDecl()->getLocation(), diag::note_entity_declared_at)
<< TT->getDecl();
Expand Down Expand Up @@ -4194,8 +4194,9 @@ static void handleCallbackAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
}

if (CalleeFnProtoType->getNumParams() != EncodingIndices.size() - 1) {
S.Diag(AL.getLoc(), diag::err_callback_attribute_wrong_arg_count)
<< QualType{CalleeFnProtoType, 0} << CalleeFnProtoType->getNumParams()
S.Diag(AL.getLoc(), diag::err_attribute_wrong_arg_count_for_func)
<< AL << QualType{CalleeFnProtoType, 0}
<< CalleeFnProtoType->getNumParams()
<< (unsigned)(EncodingIndices.size() - 1);
return;
}
Expand Down Expand Up @@ -8020,9 +8021,7 @@ void Sema::checkUnusedDeclAttributes(Declarator &D) {
}

void Sema::DiagnoseUnknownAttribute(const ParsedAttr &AL) {
std::string NormalizedFullName = '\'' + AL.getNormalizedFullName() + '\'';
SourceRange NR = AL.getNormalizedRange();

StringRef ScopeName = AL.getNormalizedScopeName();
std::optional<StringRef> CorrectedScopeName =
AL.tryGetCorrectedScopeName(ScopeName);
Expand All @@ -8044,7 +8043,7 @@ void Sema::DiagnoseUnknownAttribute(const ParsedAttr &AL) {
Diag(CorrectedScopeName ? NR.getBegin() : AL.getRange().getBegin(),
diag::warn_unknown_attribute_ignored_suggestion);

D << NormalizedFullName << CorrectedFullName;
D << AL << CorrectedFullName;

if (AL.isExplicitScope()) {
D << FixItHint::CreateReplacement(NR, CorrectedFullName) << NR;
Expand All @@ -8058,8 +8057,7 @@ void Sema::DiagnoseUnknownAttribute(const ParsedAttr &AL) {
}
}
} else {
Diag(NR.getBegin(), diag::warn_unknown_attribute_ignored)
<< NormalizedFullName << NR;
Diag(NR.getBegin(), diag::warn_unknown_attribute_ignored) << AL << NR;
}
}

Expand Down
17 changes: 13 additions & 4 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1640,6 +1640,15 @@ bool SemaHLSL::handleResourceTypeAttr(QualType T, const ParsedAttr &AL) {
return false;

Attr *A = nullptr;

AttributeCommonInfo ACI(
AL.getLoc(), AttributeScopeInfo(AL.getScopeName(), AL.getScopeLoc()),
AttributeCommonInfo::NoSemaHandlerAttribute,
{
AttributeCommonInfo::AS_CXX11, 0, false /*IsAlignas*/,
false /*IsRegularKeywordAttribute*/
});

switch (AL.getKind()) {
case ParsedAttr::AT_HLSLResourceClass: {
if (!AL.isArgIdent(0)) {
Expand All @@ -1659,16 +1668,16 @@ bool SemaHLSL::handleResourceTypeAttr(QualType T, const ParsedAttr &AL) {
<< "ResourceClass" << Identifier;
return false;
}
A = HLSLResourceClassAttr::Create(getASTContext(), RC, AL.getLoc());
A = HLSLResourceClassAttr::Create(getASTContext(), RC, ACI);
break;
}

case ParsedAttr::AT_HLSLROV:
A = HLSLROVAttr::Create(getASTContext(), AL.getLoc());
A = HLSLROVAttr::Create(getASTContext(), ACI);
break;

case ParsedAttr::AT_HLSLRawBuffer:
A = HLSLRawBufferAttr::Create(getASTContext(), AL.getLoc());
A = HLSLRawBufferAttr::Create(getASTContext(), ACI);
break;

case ParsedAttr::AT_HLSLContainedType: {
Expand All @@ -1683,7 +1692,7 @@ bool SemaHLSL::handleResourceTypeAttr(QualType T, const ParsedAttr &AL) {
if (SemaRef.RequireCompleteType(TSI->getTypeLoc().getBeginLoc(), QT,
diag::err_incomplete_type))
return false;
A = HLSLContainedTypeAttr::Create(getASTContext(), TSI, AL.getLoc());
A = HLSLContainedTypeAttr::Create(getASTContext(), TSI, ACI);
break;
}

Expand Down
2 changes: 1 addition & 1 deletion clang/test/AST/ByteCode/functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ namespace {

namespace {
/// The InitListExpr here is of void type.
void bir [[clang::annotate("B", {1, 2, 3, 4})]] (); // both-error {{'annotate' attribute requires parameter 1 to be a constant expression}} \
void bir [[clang::annotate("B", {1, 2, 3, 4})]] (); // both-error {{'clang::annotate' attribute requires parameter 1 to be a constant expression}} \
// both-note {{subexpression not valid in a constant expression}}
}

Expand Down
16 changes: 8 additions & 8 deletions clang/test/C/C23/n3037.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ union purr { float y; int x; }; // c23-error {{type 'union purr' has incompatibl

// The presence of an attribute makes two types not compatible.
struct [[gnu::packed]] attr_test { // c17-note {{previous definition is here}} \
c23-note {{attribute 'packed' here}}
c23-note {{attribute 'gnu::packed' here}}
int x;
};

Expand All @@ -75,26 +75,26 @@ struct attr_test_2 { // c17-note {{previous definition is here}}

struct [[gnu::packed]] attr_test_2 { // c17-error {{redefinition of 'attr_test_2'}} \
c23-error {{type 'struct attr_test_2' has an attribute which currently causes the types to be treated as though they are incompatible}} \
c23-note {{attribute 'packed' here}}
c23-note {{attribute 'gnu::packed' here}}
int x;
};

// This includes the same attribute on both types.
struct [[gnu::packed]] attr_test_3 { // c17-note {{previous definition is here}} \
c23-note {{attribute 'packed' here}}
c23-note {{attribute 'gnu::packed' here}}
int x;
};

struct [[gnu::packed]] attr_test_3 { // c17-error {{redefinition of 'attr_test_3'}} \
c23-error {{type 'struct attr_test_3' has an attribute which currently causes the types to be treated as though they are incompatible}} \
c23-note {{attribute 'packed' here}}
c23-note {{attribute 'gnu::packed' here}}
int x;
};

// Everything which applies to the tag itself also applies to fields.
struct field_attr_test_1 { // c17-note {{previous definition is here}}
int x;
[[gnu::packed]] int y; // c23-note {{attribute 'packed' here}}
[[gnu::packed]] int y; // c23-note {{attribute 'gnu::packed' here}}
};

struct field_attr_test_1 { // c17-error {{redefinition of 'field_attr_test_1'}} \
Expand All @@ -104,7 +104,7 @@ struct field_attr_test_1 { // c17-error {{redefinition of 'field_attr_test_1'}}
};

struct field_attr_test_2 { // c17-note {{previous definition is here}}
[[gnu::packed]] int x; // c23-note {{attribute 'packed' here}}
[[gnu::packed]] int x; // c23-note {{attribute 'gnu::packed' here}}
int y;
};

Expand All @@ -115,13 +115,13 @@ struct field_attr_test_2 { // c17-error {{redefinition of 'field_attr_test_2'}}
};

struct field_attr_test_3 { // c17-note {{previous definition is here}}
[[gnu::packed]] int x; // c23-note {{attribute 'packed' here}}
[[gnu::packed]] int x; // c23-note {{attribute 'gnu::packed' here}}
int y;
};

struct field_attr_test_3 { // c17-error {{redefinition of 'field_attr_test_3'}} \
c23-error {{type 'struct field_attr_test_3' has a member with an attribute which currently causes the types to be treated as though they are incompatible}}
int x [[gnu::packed]]; // c23-note {{attribute 'packed' here}}
int x [[gnu::packed]]; // c23-note {{attribute 'gnu::packed' here}}
int y;
};

Expand Down
2 changes: 1 addition & 1 deletion clang/test/CXX/expr/expr.prim/expr.prim.lambda/p5.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ void test_attributes() {
auto nrl = [](int x) -> int { if (x > 0) return x; }; // expected-error{{non-void lambda does not return a value in all control paths}}

// FIXME: GCC accepts the [[gnu::noreturn]] attribute here.
auto nrl2 = []() [[gnu::noreturn]] { return; }; // expected-warning{{attribute 'noreturn' ignored}}
auto nrl2 = []() [[gnu::noreturn]] { return; }; // expected-warning{{attribute 'gnu::noreturn' ignored}}
}

template<typename T>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
template<typename T>
void test_attributes() {
// FIXME: GCC accepts [[gnu::noreturn]] here.
auto nrl = []() [[gnu::noreturn]] {}; // expected-warning{{attribute 'noreturn' ignored}}
auto nrl = []() [[gnu::noreturn]] {}; // expected-warning{{attribute 'gnu::noreturn' ignored}}
}

template void test_attributes<int>();
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGen/RISCV/riscv-vector-callingconv.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ void __attribute__((riscv_vector_cc)) test_no_attribute(int x) { } // expected-e
[[riscv::vector_cc]] int var2; // expected-warning {{'vector_cc' only applies to function types; type here is 'int'}}

[[riscv::vector_cc]] void func2();
[[riscv::vector_cc(1)]] void func_invalid2(); // expected-error {{'vector_cc' attribute takes no arguments}}
[[riscv::vector_cc(1)]] void func_invalid2(); // expected-error {{'riscv::vector_cc' attribute takes no arguments}}

void test_no_attribute2(int); // expected-note {{previous declaration is here}}
[[riscv::vector_cc]] void test_no_attribute2(int x) { } // expected-error {{function declared 'riscv_vector_cc' here was previously declared without calling convention}}
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGen/RISCV/riscv-vector-callingconv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void test_lambda() {
[[riscv::vector_cc]] int var2; // expected-warning {{'vector_cc' only applies to function types; type here is 'int'}}

[[riscv::vector_cc]] void func2();
[[riscv::vector_cc(1)]] void func_invalid2(); // expected-error {{'vector_cc' attribute takes no arguments}}
[[riscv::vector_cc(1)]] void func_invalid2(); // expected-error {{'riscv::vector_cc' attribute takes no arguments}}

void test_no_attribute2(int); // expected-note {{previous declaration is here}}
[[riscv::vector_cc]] void test_no_attribute2(int x) { } // expected-error {{function declared 'riscv_vector_cc' here was previously declared without calling convention}}
Expand Down
10 changes: 5 additions & 5 deletions clang/test/Frontend/noderef.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,18 +227,18 @@ int test(void) {
// ignored.
// For details see https://github.com/llvm/llvm-project/issues/55790
void test_standard_syntax() {
[[clang::noderef]] int i; // expected-warning {{'noderef' attribute ignored}}
[[clang::noderef]] int i; // expected-warning {{'clang::noderef' attribute ignored}}

[[clang::noderef]] int *p1; // expected-warning {{'noderef' attribute ignored}}
[[clang::noderef]] int *p1; // expected-warning {{'clang::noderef' attribute ignored}}
*p1;

int *p2 [[clang::noderef]]; // expected-warning {{'noderef' attribute ignored}}
int *p2 [[clang::noderef]]; // expected-warning {{'clang::noderef' attribute ignored}}
*p2;

int * [[clang::noderef]] p3; // expected-warning {{'noderef' attribute ignored}}
int * [[clang::noderef]] p3; // expected-warning {{'clang::noderef' attribute ignored}}
*p3;

typedef int* IntPtr;
[[clang::noderef]] IntPtr p4; // expected-warning {{'noderef' attribute ignored}}
[[clang::noderef]] IntPtr p4; // expected-warning {{'clang::noderef' attribute ignored}}
*p4;
}
Loading