60
60
using namespace hilti ;
61
61
using util::fmt;
62
62
63
+ /* *
64
+ * A mapping of node tags to any attributes that node allows. When a new
65
+ * attribute is added, this map must be updated to accept that attribute on any
66
+ * nodes it applies to.
67
+ */
68
+ static std::unordered_map<node::Tag, std::unordered_set<Attribute::Kind>> allowed_attributes{
69
+ {node::tag::Function,
70
+ {Attribute::Kind::Cxxname, Attribute::Kind::HavePrototype, Attribute::Kind::Priority, Attribute::Kind::Static,
71
+ Attribute::Kind::NeededByFeature, Attribute::Kind::Debug, Attribute::Kind::Foreach, Attribute::Kind::Error}},
72
+ {node::tag::declaration::Parameter, {Attribute::Kind::RequiresTypeFeature}},
73
+ };
74
+
63
75
void hilti::validator::VisitorMixIn::deprecated (const std::string& msg, const Location& l) const {
64
76
hilti::logger ().deprecated (msg, l);
65
77
}
@@ -124,6 +136,30 @@ struct VisitorPre : visitor::PreOrder, public validator::VisitorMixIn {
124
136
struct VisitorPost : visitor::PreOrder, public validator::VisitorMixIn {
125
137
using hilti::validator::VisitorMixIn::VisitorMixIn;
126
138
139
+ // Ensures that the node represented by tag is allowed to have all of the
140
+ // provided attributes. This does not use any context, if more information
141
+ // is needed, then do the check elsewhere.
142
+ void checkNodeAttributes (node::Tag tag, AttributeSet* attributes, const std::string_view& where) {
143
+ if ( ! attributes )
144
+ return ;
145
+
146
+ auto it = allowed_attributes.find (tag);
147
+
148
+ if ( it == allowed_attributes.end () ) {
149
+ if ( ! attributes->attributes ().empty () )
150
+ error (hilti::util::fmt (" No attributes expected in %s" , where), attributes);
151
+
152
+ return ;
153
+ }
154
+
155
+ auto allowed = it->second ;
156
+
157
+ for ( const auto & attr : attributes->attributes () ) {
158
+ if ( allowed.find (attr->kind ()) == allowed.end () )
159
+ error (hilti::util::fmt (" invalid attribute '%s' in %s" , attr->attributeName (), where), attr);
160
+ }
161
+ }
162
+
127
163
// Returns an error if the given type cannot be used for ordering at
128
164
// runtime.
129
165
Result<Nothing> isSortable (QualifiedType* t) {
@@ -176,6 +212,8 @@ struct VisitorPost : visitor::PreOrder, public validator::VisitorMixIn {
176
212
}
177
213
178
214
void operator ()(Function* n) final {
215
+ checkNodeAttributes (n->nodeTag (), n->attributes (), " function" );
216
+
179
217
if ( auto attrs = n->attributes () ) {
180
218
if ( auto prio = attrs->find (hilti::Attribute::Kind::Priority) ) {
181
219
if ( n->ftype ()->flavor () != type::function::Flavor::Hook )
@@ -260,6 +298,8 @@ struct VisitorPost : visitor::PreOrder, public validator::VisitorMixIn {
260
298
}
261
299
262
300
void operator ()(declaration::Parameter* n) final {
301
+ checkNodeAttributes (n->nodeTag (), n->attributes (), n->displayName ());
302
+
263
303
if ( ! n->type ()->type ()->isA <type::Auto>() ) {
264
304
if ( ! n->type ()->type ()->isAllocable () && ! n->type ()->type ()->isA <type::Any>() )
265
305
error (fmt (" type '%s' cannot be used for function parameter" , *n->type ()), n);
@@ -285,10 +325,7 @@ struct VisitorPost : visitor::PreOrder, public validator::VisitorMixIn {
285
325
286
326
if ( auto attrs = n->attributes () )
287
327
for ( const auto & attr : attrs->attributes () ) {
288
- if ( attr->kind () != hilti::Attribute::Kind::RequiresTypeFeature )
289
- error (fmt (" invalid attribute '%s' for function parameter" , attr->attributeName ()), n);
290
-
291
- else {
328
+ if ( attr->kind () == hilti::Attribute::Kind::RequiresTypeFeature ) {
292
329
if ( auto x = attr->valueAsString (); ! x )
293
330
error (x.error (), n);
294
331
}
0 commit comments