Skip to content

Commit e399b89

Browse files
authored
Merge pull request #5655 from compnerd/attributes
parse: process GNU and standard attributes on top-level decls
2 parents a8f1ed4 + 5671239 commit e399b89

File tree

11 files changed

+145
-71
lines changed

11 files changed

+145
-71
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,9 @@ Bug Fixes
248248
not satisfied in the event of an instantiation failures in a requires expression's
249249
parameter list. We previously handled this correctly in a constraint evaluation
250250
context, but not in a requires clause evaluated as a boolean.
251+
- GNU attributes being applied prior to standard attributes would be handled
252+
improperly, which was corrected to match the behaviour exhibited by GCC.
253+
`Issue 58229 <https://github.com/llvm/llvm-project/issues/58229>`_
251254

252255
Improvements to Clang's diagnostics
253256
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Parse/Parser.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,15 +1601,16 @@ class Parser : public CodeCompletionHandler {
16011601

16021602
//===--------------------------------------------------------------------===//
16031603
// C99 6.9: External Definitions.
1604-
DeclGroupPtrTy ParseExternalDeclaration(ParsedAttributes &Attrs,
1604+
DeclGroupPtrTy ParseExternalDeclaration(ParsedAttributes &DeclAttrs,
1605+
ParsedAttributes &DeclSpecAttrs,
16051606
ParsingDeclSpec *DS = nullptr);
16061607
bool isDeclarationAfterDeclarator();
16071608
bool isStartOfFunctionDefinition(const ParsingDeclarator &Declarator);
1608-
DeclGroupPtrTy
1609-
ParseDeclarationOrFunctionDefinition(ParsedAttributes &Attrs,
1610-
ParsingDeclSpec *DS = nullptr,
1611-
AccessSpecifier AS = AS_none);
1609+
DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(
1610+
ParsedAttributes &DeclAttrs, ParsedAttributes &DeclSpecAttrs,
1611+
ParsingDeclSpec *DS = nullptr, AccessSpecifier AS = AS_none);
16121612
DeclGroupPtrTy ParseDeclOrFunctionDefInternal(ParsedAttributes &Attrs,
1613+
ParsedAttributes &DeclSpecAttrs,
16131614
ParsingDeclSpec &DS,
16141615
AccessSpecifier AS);
16151616

@@ -1624,7 +1625,8 @@ class Parser : public CodeCompletionHandler {
16241625

16251626
// Objective-C External Declarations
16261627
void MaybeSkipAttributes(tok::ObjCKeywordKind Kind);
1627-
DeclGroupPtrTy ParseObjCAtDirectives(ParsedAttributes &Attrs);
1628+
DeclGroupPtrTy ParseObjCAtDirectives(ParsedAttributes &DeclAttrs,
1629+
ParsedAttributes &DeclSpecAttrs);
16281630
DeclGroupPtrTy ParseObjCAtClassDeclaration(SourceLocation atLoc);
16291631
Decl *ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc,
16301632
ParsedAttributes &prefixAttrs);

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -254,9 +254,10 @@ void Parser::ParseInnerNamespace(const InnerNamespaceInfoList &InnerNSs,
254254
if (index == InnerNSs.size()) {
255255
while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
256256
Tok.isNot(tok::eof)) {
257-
ParsedAttributes Attrs(AttrFactory);
258-
MaybeParseCXX11Attributes(Attrs);
259-
ParseExternalDeclaration(Attrs);
257+
ParsedAttributes DeclAttrs(AttrFactory);
258+
MaybeParseCXX11Attributes(DeclAttrs);
259+
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
260+
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
260261
}
261262

262263
// The caller is what called check -- we are simply calling
@@ -359,6 +360,7 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context) {
359360

360361
ParsedAttributes DeclAttrs(AttrFactory);
361362
MaybeParseCXX11Attributes(DeclAttrs);
363+
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
362364

363365
if (Tok.isNot(tok::l_brace)) {
364366
// Reset the source range in DS, as the leading "extern"
@@ -367,7 +369,7 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context) {
367369
DS.SetRangeEnd(SourceLocation());
368370
// ... but anyway remember that such an "extern" was seen.
369371
DS.setExternInLinkageSpec(true);
370-
ParseExternalDeclaration(DeclAttrs, &DS);
372+
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs, &DS);
371373
return LinkageSpec ? Actions.ActOnFinishLinkageSpecification(
372374
getCurScope(), LinkageSpec, SourceLocation())
373375
: nullptr;
@@ -407,9 +409,9 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context) {
407409
break;
408410
[[fallthrough]];
409411
default:
410-
ParsedAttributes Attrs(AttrFactory);
411-
MaybeParseCXX11Attributes(Attrs);
412-
ParseExternalDeclaration(Attrs);
412+
ParsedAttributes DeclAttrs(AttrFactory);
413+
MaybeParseCXX11Attributes(DeclAttrs);
414+
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
413415
continue;
414416
}
415417

@@ -439,9 +441,10 @@ Decl *Parser::ParseExportDeclaration() {
439441

440442
if (Tok.isNot(tok::l_brace)) {
441443
// FIXME: Factor out a ParseExternalDeclarationWithAttrs.
442-
ParsedAttributes Attrs(AttrFactory);
443-
MaybeParseCXX11Attributes(Attrs);
444-
ParseExternalDeclaration(Attrs);
444+
ParsedAttributes DeclAttrs(AttrFactory);
445+
MaybeParseCXX11Attributes(DeclAttrs);
446+
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
447+
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
445448
return Actions.ActOnFinishExportDecl(getCurScope(), ExportDecl,
446449
SourceLocation());
447450
}
@@ -458,9 +461,10 @@ Decl *Parser::ParseExportDeclaration() {
458461

459462
while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
460463
Tok.isNot(tok::eof)) {
461-
ParsedAttributes Attrs(AttrFactory);
462-
MaybeParseCXX11Attributes(Attrs);
463-
ParseExternalDeclaration(Attrs);
464+
ParsedAttributes DeclAttrs(AttrFactory);
465+
MaybeParseCXX11Attributes(DeclAttrs);
466+
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
467+
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
464468
}
465469

466470
T.consumeClose();

clang/lib/Parse/ParseHLSL.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,11 @@ Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd) {
7777

7878
while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
7979
// FIXME: support attribute on constants inside cbuffer/tbuffer.
80-
ParsedAttributes Attrs(AttrFactory);
80+
ParsedAttributes DeclAttrs(AttrFactory);
81+
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
8182

82-
DeclGroupPtrTy Result = ParseExternalDeclaration(Attrs);
83+
DeclGroupPtrTy Result =
84+
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
8385
if (!validateDeclsInsideHLSLBuffer(Result, IdentifierLoc, IsCBuffer,
8486
*this)) {
8587
T.skipToEnd();

clang/lib/Parse/ParseObjc.cpp

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ void Parser::MaybeSkipAttributes(tok::ObjCKeywordKind Kind) {
4545
/// [OBJC] objc-protocol-definition
4646
/// [OBJC] objc-method-definition
4747
/// [OBJC] '@' 'end'
48-
Parser::DeclGroupPtrTy Parser::ParseObjCAtDirectives(ParsedAttributes &Attrs) {
48+
Parser::DeclGroupPtrTy
49+
Parser::ParseObjCAtDirectives(ParsedAttributes &DeclAttrs,
50+
ParsedAttributes &DeclSpecAttrs) {
51+
DeclAttrs.takeAllFrom(DeclSpecAttrs);
52+
4953
SourceLocation AtLoc = ConsumeToken(); // the "@"
5054

5155
if (Tok.is(tok::code_completion)) {
@@ -54,17 +58,29 @@ Parser::DeclGroupPtrTy Parser::ParseObjCAtDirectives(ParsedAttributes &Attrs) {
5458
return nullptr;
5559
}
5660

61+
switch (Tok.getObjCKeywordID()) {
62+
case tok::objc_interface:
63+
case tok::objc_protocol:
64+
case tok::objc_implementation:
65+
break;
66+
default:
67+
llvm::for_each(DeclAttrs, [this](const auto &Attr) {
68+
if (Attr.isGNUAttribute())
69+
Diag(Tok.getLocation(), diag::err_objc_unexpected_attr);
70+
});
71+
}
72+
5773
Decl *SingleDecl = nullptr;
5874
switch (Tok.getObjCKeywordID()) {
5975
case tok::objc_class:
6076
return ParseObjCAtClassDeclaration(AtLoc);
6177
case tok::objc_interface:
62-
SingleDecl = ParseObjCAtInterfaceDeclaration(AtLoc, Attrs);
78+
SingleDecl = ParseObjCAtInterfaceDeclaration(AtLoc, DeclAttrs);
6379
break;
6480
case tok::objc_protocol:
65-
return ParseObjCAtProtocolDeclaration(AtLoc, Attrs);
81+
return ParseObjCAtProtocolDeclaration(AtLoc, DeclAttrs);
6682
case tok::objc_implementation:
67-
return ParseObjCAtImplementationDeclaration(AtLoc, Attrs);
83+
return ParseObjCAtImplementationDeclaration(AtLoc, DeclAttrs);
6884
case tok::objc_end:
6985
return ParseObjCAtEndDeclaration(AtLoc);
7086
case tok::objc_compatibility_alias:
@@ -652,21 +668,23 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
652668
if (Tok.is(tok::r_brace))
653669
break;
654670

655-
ParsedAttributes EmptyAttrs(AttrFactory);
671+
ParsedAttributes EmptyDeclAttrs(AttrFactory);
672+
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
656673

657674
// Since we call ParseDeclarationOrFunctionDefinition() instead of
658675
// ParseExternalDeclaration() below (so that this doesn't parse nested
659676
// @interfaces), this needs to duplicate some code from the latter.
660677
if (Tok.isOneOf(tok::kw_static_assert, tok::kw__Static_assert)) {
661678
SourceLocation DeclEnd;
662679
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
663-
allTUVariables.push_back(ParseDeclaration(
664-
DeclaratorContext::File, DeclEnd, EmptyAttrs, EmptyDeclSpecAttrs));
680+
allTUVariables.push_back(ParseDeclaration(DeclaratorContext::File,
681+
DeclEnd, EmptyDeclAttrs,
682+
EmptyDeclSpecAttrs));
665683
continue;
666684
}
667685

668-
allTUVariables.push_back(
669-
ParseDeclarationOrFunctionDefinition(EmptyAttrs));
686+
allTUVariables.push_back(ParseDeclarationOrFunctionDefinition(
687+
EmptyDeclAttrs, EmptyDeclSpecAttrs));
670688
continue;
671689
}
672690

@@ -2225,9 +2243,11 @@ Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc,
22252243
{
22262244
ObjCImplParsingDataRAII ObjCImplParsing(*this, ObjCImpDecl);
22272245
while (!ObjCImplParsing.isFinished() && !isEofOrEom()) {
2228-
ParsedAttributes attrs(AttrFactory);
2229-
MaybeParseCXX11Attributes(attrs);
2230-
if (DeclGroupPtrTy DGP = ParseExternalDeclaration(attrs)) {
2246+
ParsedAttributes DeclAttrs(AttrFactory);
2247+
MaybeParseCXX11Attributes(DeclAttrs);
2248+
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
2249+
if (DeclGroupPtrTy DGP =
2250+
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs)) {
22312251
DeclGroupRef DG = DGP.get();
22322252
DeclsInGroup.append(DG.begin(), DG.end());
22332253
}

clang/lib/Parse/ParseOpenMP.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2245,9 +2245,10 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl(
22452245
// Here we expect to see some function declaration.
22462246
if (AS == AS_none) {
22472247
assert(TagType == DeclSpec::TST_unspecified);
2248+
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
22482249
MaybeParseCXX11Attributes(Attrs);
22492250
ParsingDeclSpec PDS(*this);
2250-
Ptr = ParseExternalDeclaration(Attrs, &PDS);
2251+
Ptr = ParseExternalDeclaration(Attrs, EmptyDeclSpecAttrs, &PDS);
22512252
} else {
22522253
Ptr =
22532254
ParseCXXClassMemberDeclarationWithPragmas(AS, Attrs, TagType, Tag);

clang/lib/Parse/Parser.cpp

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -739,10 +739,17 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,
739739
break;
740740
}
741741

742-
ParsedAttributes attrs(AttrFactory);
743-
MaybeParseCXX11Attributes(attrs);
744-
745-
Result = ParseExternalDeclaration(attrs);
742+
ParsedAttributes DeclAttrs(AttrFactory);
743+
ParsedAttributes DeclSpecAttrs(AttrFactory);
744+
// GNU attributes are applied to the declaration specification while the
745+
// standard attributes are applied to the declaration. We parse the two
746+
// attribute sets into different containters so we can apply them during
747+
// the regular parsing process.
748+
while (MaybeParseCXX11Attributes(DeclAttrs) ||
749+
MaybeParseGNUAttributes(DeclSpecAttrs))
750+
;
751+
752+
Result = ParseExternalDeclaration(DeclAttrs, DeclSpecAttrs);
746753
// An empty Result might mean a line with ';' or some parsing error, ignore
747754
// it.
748755
if (Result) {
@@ -785,8 +792,10 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,
785792
///
786793
/// [Modules-TS] module-import-declaration
787794
///
788-
Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
789-
ParsingDeclSpec *DS) {
795+
Parser::DeclGroupPtrTy
796+
Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
797+
ParsedAttributes &DeclSpecAttrs,
798+
ParsingDeclSpec *DS) {
790799
DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(*this);
791800
ParenBraceBracketBalancer BalancerRAIIObj(*this);
792801

@@ -874,7 +883,7 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
874883
// __extension__ silences extension warnings in the subexpression.
875884
ExtensionRAIIObject O(Diags); // Use RAII to do this.
876885
ConsumeToken();
877-
return ParseExternalDeclaration(Attrs);
886+
return ParseExternalDeclaration(Attrs, DeclSpecAttrs);
878887
}
879888
case tok::kw_asm: {
880889
ProhibitAttributes(Attrs);
@@ -902,7 +911,7 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
902911
break;
903912
}
904913
case tok::at:
905-
return ParseObjCAtDirectives(Attrs);
914+
return ParseObjCAtDirectives(Attrs, DeclSpecAttrs);
906915
case tok::minus:
907916
case tok::plus:
908917
if (!getLangOpts().ObjC) {
@@ -950,18 +959,16 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
950959
// A function definition cannot start with any of these keywords.
951960
{
952961
SourceLocation DeclEnd;
953-
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
954962
return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
955-
EmptyDeclSpecAttrs);
963+
DeclSpecAttrs);
956964
}
957965

958966
case tok::kw_cbuffer:
959967
case tok::kw_tbuffer:
960968
if (getLangOpts().HLSL) {
961969
SourceLocation DeclEnd;
962-
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
963970
return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
964-
EmptyDeclSpecAttrs);
971+
DeclSpecAttrs);
965972
}
966973
goto dont_know;
967974

@@ -972,9 +979,8 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
972979
Diag(ConsumeToken(), diag::warn_static_inline_explicit_inst_ignored)
973980
<< 0;
974981
SourceLocation DeclEnd;
975-
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
976982
return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
977-
EmptyDeclSpecAttrs);
983+
DeclSpecAttrs);
978984
}
979985
goto dont_know;
980986

@@ -985,9 +991,8 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
985991
// Inline namespaces. Allowed as an extension even in C++03.
986992
if (NextKind == tok::kw_namespace) {
987993
SourceLocation DeclEnd;
988-
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
989994
return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
990-
EmptyDeclSpecAttrs);
995+
DeclSpecAttrs);
991996
}
992997

993998
// Parse (then ignore) 'inline' prior to a template instantiation. This is
@@ -996,9 +1001,8 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
9961001
Diag(ConsumeToken(), diag::warn_static_inline_explicit_inst_ignored)
9971002
<< 1;
9981003
SourceLocation DeclEnd;
999-
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
10001004
return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
1001-
EmptyDeclSpecAttrs);
1005+
DeclSpecAttrs);
10021006
}
10031007
}
10041008
goto dont_know;
@@ -1034,7 +1038,7 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
10341038
return nullptr;
10351039
}
10361040
// We can't tell whether this is a function-definition or declaration yet.
1037-
return ParseDeclarationOrFunctionDefinition(Attrs, DS);
1041+
return ParseDeclarationOrFunctionDefinition(Attrs, DeclSpecAttrs, DS);
10381042
}
10391043

10401044
// This routine returns a DeclGroup, if the thing we parsed only contains a
@@ -1099,7 +1103,17 @@ bool Parser::isStartOfFunctionDefinition(const ParsingDeclarator &Declarator) {
10991103
/// [OMP] allocate-directive [TODO]
11001104
///
11011105
Parser::DeclGroupPtrTy Parser::ParseDeclOrFunctionDefInternal(
1102-
ParsedAttributes &Attrs, ParsingDeclSpec &DS, AccessSpecifier AS) {
1106+
ParsedAttributes &Attrs, ParsedAttributes &DeclSpecAttrs,
1107+
ParsingDeclSpec &DS, AccessSpecifier AS) {
1108+
// Because we assume that the DeclSpec has not yet been initialised, we simply
1109+
// overwrite the source range and attribute the provided leading declspec
1110+
// attributes.
1111+
assert(DS.getSourceRange().isInvalid() &&
1112+
"expected uninitialised source range");
1113+
DS.SetRangeStart(DeclSpecAttrs.Range.getBegin());
1114+
DS.SetRangeEnd(DeclSpecAttrs.Range.getEnd());
1115+
DS.takeAttributesFrom(DeclSpecAttrs);
1116+
11031117
MaybeParseMicrosoftAttributes(DS.getAttributes());
11041118
// Parse the common declaration-specifiers piece.
11051119
ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS,
@@ -1198,17 +1212,18 @@ Parser::DeclGroupPtrTy Parser::ParseDeclOrFunctionDefInternal(
11981212
}
11991213

12001214
Parser::DeclGroupPtrTy Parser::ParseDeclarationOrFunctionDefinition(
1201-
ParsedAttributes &Attrs, ParsingDeclSpec *DS, AccessSpecifier AS) {
1215+
ParsedAttributes &Attrs, ParsedAttributes &DeclSpecAttrs,
1216+
ParsingDeclSpec *DS, AccessSpecifier AS) {
12021217
if (DS) {
1203-
return ParseDeclOrFunctionDefInternal(Attrs, *DS, AS);
1218+
return ParseDeclOrFunctionDefInternal(Attrs, DeclSpecAttrs, *DS, AS);
12041219
} else {
12051220
ParsingDeclSpec PDS(*this);
12061221
// Must temporarily exit the objective-c container scope for
12071222
// parsing c constructs and re-enter objc container scope
12081223
// afterwards.
12091224
ObjCDeclContextSwitch ObjCDC(*this);
12101225

1211-
return ParseDeclOrFunctionDefInternal(Attrs, PDS, AS);
1226+
return ParseDeclOrFunctionDefInternal(Attrs, DeclSpecAttrs, PDS, AS);
12121227
}
12131228
}
12141229

@@ -2350,7 +2365,8 @@ void Parser::ParseMicrosoftIfExistsExternalDeclaration() {
23502365
while (Tok.isNot(tok::r_brace) && !isEofOrEom()) {
23512366
ParsedAttributes Attrs(AttrFactory);
23522367
MaybeParseCXX11Attributes(Attrs);
2353-
DeclGroupPtrTy Result = ParseExternalDeclaration(Attrs);
2368+
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
2369+
DeclGroupPtrTy Result = ParseExternalDeclaration(Attrs, EmptyDeclSpecAttrs);
23542370
if (Result && !getCurScope()->getParent())
23552371
Actions.getASTConsumer().HandleTopLevelDecl(Result.get());
23562372
}

0 commit comments

Comments
 (0)