diff --git a/src/Expr.cc b/src/Expr.cc index 0d5f72acf9..02517b4286 100644 --- a/src/Expr.cc +++ b/src/Expr.cc @@ -2884,9 +2884,8 @@ FieldExpr::FieldExpr(Expr* arg_op, const char* arg_field_name) SetType(rt->FieldType(field)->Ref()); td = rt->FieldDecl(field); - if ( td->FindAttr(ATTR_DEPRECATED) ) - reporter->Warning("deprecated (%s$%s)", rt->GetName().c_str(), - field_name); + if ( rt->IsFieldDeprecated(field) ) + reporter->Warning("%s", rt->GetFieldDeprecationWarning(field, false).c_str()); } } } @@ -2975,9 +2974,8 @@ HasFieldExpr::HasFieldExpr(Expr* arg_op, const char* arg_field_name) if ( field < 0 ) ExprError("no such field in record"); - else if ( rt->FieldDecl(field)->FindAttr(ATTR_DEPRECATED) ) - reporter->Warning("deprecated (%s?$%s)", rt->GetName().c_str(), - field_name); + else if ( rt->IsFieldDeprecated(field) ) + reporter->Warning("%s", rt->GetFieldDeprecationWarning(field, true).c_str()); SetType(base_type(TYPE_BOOL)); } @@ -3659,13 +3657,8 @@ RecordCoerceExpr::RecordCoerceExpr(Expr* op, RecordType* r) break; } } - else - { - if ( t_r->FieldDecl(i)->FindAttr(ATTR_DEPRECATED) ) - reporter->Warning("deprecated (%s$%s)", - t_r->GetName().c_str(), - t_r->FieldName(i)); - } + else if ( t_r->IsFieldDeprecated(i) ) + reporter->Warning("%s", t_r->GetFieldDeprecationWarning(i, false).c_str()); } } } diff --git a/src/ID.cc b/src/ID.cc index 894de949a5..e6392bc964 100644 --- a/src/ID.cc +++ b/src/ID.cc @@ -189,15 +189,35 @@ void ID::UpdateValAttrs() } } -void ID::MakeDeprecated() +void ID::MakeDeprecated(Expr* deprecation) { if ( IsDeprecated() ) return; - attr_list* attr = new attr_list{new Attr(ATTR_DEPRECATED)}; + attr_list* attr = new attr_list{new Attr(ATTR_DEPRECATED, deprecation)}; AddAttrs(new Attributes(attr, Type(), false)); } +string ID::GetDeprecationWarning() const + { + string result; + Attr* depr_attr = FindAttr(ATTR_DEPRECATED); + if ( depr_attr ) + { + ConstExpr* expr = static_cast(depr_attr->AttrExpr()); + if ( expr ) + { + StringVal* text = expr->Value()->AsStringVal(); + result = text->CheckString(); + } + } + + if ( result.empty() ) + return fmt("deprecated (%s)", Name()); + else + return fmt("deprecated (%s): %s", Name(), result.c_str()); + } + void ID::AddAttrs(Attributes* a) { if ( attrs ) diff --git a/src/ID.h b/src/ID.h index ee60d4e61c..b90e5d9597 100644 --- a/src/ID.h +++ b/src/ID.h @@ -82,7 +82,9 @@ public: bool IsDeprecated() const { return FindAttr(ATTR_DEPRECATED) != 0; } - void MakeDeprecated(); + void MakeDeprecated(Expr* deprecation); + + string GetDeprecationWarning() const; void Error(const char* msg, const BroObj* o2 = 0); diff --git a/src/Type.cc b/src/Type.cc index 7b65dd0ffa..60461e026f 100644 --- a/src/Type.cc +++ b/src/Type.cc @@ -985,6 +985,33 @@ void RecordType::DescribeFieldsReST(ODesc* d, bool func_args) const d->PopIndentNoNL(); } +string RecordType::GetFieldDeprecationWarning(int field, bool has_check) const + { + const TypeDecl* decl = FieldDecl(field); + if ( decl) + { + string result; + if ( const Attr* deprecation = decl->FindAttr(ATTR_DEPRECATED) ) + { + ConstExpr* expr = static_cast(deprecation->AttrExpr()); + if ( expr ) + { + StringVal* text = expr->Value()->AsStringVal(); + result = text->CheckString(); + } + } + + if ( result.empty() ) + return fmt("deprecated (%s%s$%s)", GetName().c_str(), has_check ? "?" : "", + FieldName(field)); + else + return fmt("deprecated (%s%s$%s): %s", GetName().c_str(), has_check ? "?" : "", + FieldName(field), result.c_str()); + } + + return ""; + } + SubNetType::SubNetType() : BroType(TYPE_SUBNET) { } @@ -1083,7 +1110,7 @@ EnumType::~EnumType() // Note, we use reporter->Error() here (not Error()) to include the current script // location in the error message, rather than the one where the type was // originally defined. -void EnumType::AddName(const string& module_name, const char* name, bool is_export, bool deprecated) +void EnumType::AddName(const string& module_name, const char* name, bool is_export, Expr* deprecation) { /* implicit, auto-increment */ if ( counter < 0) @@ -1092,11 +1119,11 @@ void EnumType::AddName(const string& module_name, const char* name, bool is_expo SetError(); return; } - CheckAndAddName(module_name, name, counter, is_export, deprecated); + CheckAndAddName(module_name, name, counter, is_export, deprecation); counter++; } -void EnumType::AddName(const string& module_name, const char* name, bro_int_t val, bool is_export, bool deprecated) +void EnumType::AddName(const string& module_name, const char* name, bro_int_t val, bool is_export, Expr* deprecation) { /* explicit value specified */ if ( counter > 0 ) @@ -1106,11 +1133,11 @@ void EnumType::AddName(const string& module_name, const char* name, bro_int_t va return; } counter = -1; - CheckAndAddName(module_name, name, val, is_export, deprecated); + CheckAndAddName(module_name, name, val, is_export, deprecation); } void EnumType::CheckAndAddName(const string& module_name, const char* name, - bro_int_t val, bool is_export, bool deprecated) + bro_int_t val, bool is_export, Expr* deprecation) { if ( Lookup(val) ) { @@ -1127,8 +1154,8 @@ void EnumType::CheckAndAddName(const string& module_name, const char* name, id->SetType(this->Ref()); id->SetEnumConst(); - if ( deprecated ) - id->MakeDeprecated(); + if ( deprecation ) + id->MakeDeprecated(deprecation); zeekygen_mgr->Identifier(id); } diff --git a/src/Type.h b/src/Type.h index 80548799a0..d22178a13e 100644 --- a/src/Type.h +++ b/src/Type.h @@ -494,6 +494,14 @@ public: void DescribeFields(ODesc* d) const; void DescribeFieldsReST(ODesc* d, bool func_args) const; + bool IsFieldDeprecated(int field) const + { + const TypeDecl* decl = FieldDecl(field); + return decl && decl->FindAttr(ATTR_DEPRECATED) != 0; + } + + string GetFieldDeprecationWarning(int field, bool has_check) const; + protected: RecordType() { types = 0; } @@ -551,12 +559,12 @@ public: // The value of this name is next internal counter value, starting // with zero. The internal counter is incremented. - void AddName(const string& module_name, const char* name, bool is_export, bool deprecated); + void AddName(const string& module_name, const char* name, bool is_export, Expr* deprecation = nullptr); // The value of this name is set to val. Once a value has been // explicitly assigned using this method, no further names can be // added that aren't likewise explicitly initalized. - void AddName(const string& module_name, const char* name, bro_int_t val, bool is_export, bool deprecated); + void AddName(const string& module_name, const char* name, bro_int_t val, bool is_export, Expr* deprecation = nullptr); // -1 indicates not found. bro_int_t Lookup(const string& module_name, const char* name) const; @@ -578,7 +586,7 @@ protected: void CheckAndAddName(const string& module_name, const char* name, bro_int_t val, bool is_export, - bool deprecated); + Expr* deprecation = nullptr); typedef std::map NameMap; NameMap names; diff --git a/src/Var.cc b/src/Var.cc index ca8ed38944..ab628d7936 100644 --- a/src/Var.cc +++ b/src/Var.cc @@ -312,16 +312,21 @@ static void transfer_arg_defaults(RecordType* args, RecordType* recv) } } -static bool has_attr(const attr_list* al, attr_tag tag) +static Attr* find_attr(const attr_list* al, attr_tag tag) { if ( ! al ) - return false; + return nullptr; for ( int i = 0; i < al->length(); ++i ) if ( (*al)[i]->Tag() == tag ) - return true; + return (*al)[i]; - return false; + return nullptr; + } + +static bool has_attr(const attr_list* al, attr_tag tag) + { + return find_attr(al, tag) != nullptr; } void begin_func(ID* id, const char* module_name, function_flavor flavor, @@ -399,8 +404,8 @@ void begin_func(ID* id, const char* module_name, function_flavor flavor, arg_id->SetType(arg_i->type->Ref()); } - if ( has_attr(attrs, ATTR_DEPRECATED) ) - id->MakeDeprecated(); + if ( Attr* depr_attr = find_attr(attrs, ATTR_DEPRECATED) ) + id->MakeDeprecated(depr_attr->AttrExpr()); } class OuterIDBindingFinder : public TraversalCallback { diff --git a/src/parse.y b/src/parse.y index b1662f2c94..da108924ad 100644 --- a/src/parse.y +++ b/src/parse.y @@ -5,7 +5,7 @@ // Switching parser table type fixes ambiguity problems. %define lr.type ielr -%expect 104 +%expect 105 %token TOK_ADD TOK_ADD_TO TOK_ADDR TOK_ANY %token TOK_ATENDIF TOK_ATELSE TOK_ATIF TOK_ATIFDEF TOK_ATIFNDEF @@ -50,14 +50,14 @@ %left '$' '[' ']' '(' ')' TOK_HAS_FIELD TOK_HAS_ATTR %nonassoc TOK_AS TOK_IS -%type opt_no_test opt_no_test_block opt_deprecated TOK_PATTERN_END +%type opt_no_test opt_no_test_block TOK_PATTERN_END %type TOK_ID TOK_PATTERN_TEXT %type local_id global_id def_global_id event_id global_or_event_id resolve_id begin_func case_type %type local_id_list case_type_list %type init_class %type opt_init %type TOK_CONSTANT -%type expr opt_expr init anonymous_function index_slice +%type expr opt_expr init anonymous_function index_slice opt_deprecated %type event %type stmt stmt_list func_body for_head %type type opt_type enum_body @@ -700,7 +700,7 @@ expr: $$ = new NameExpr(id); if ( id->IsDeprecated() ) - reporter->Warning("deprecated (%s)", id->Name()); + reporter->Warning("%s", id->GetDeprecationWarning().c_str()); } } @@ -1002,7 +1002,7 @@ type: Ref($$); if ( $1->IsDeprecated() ) - reporter->Warning("deprecated (%s)", $1->Name()); + reporter->Warning("%s", $1->GetDeprecationWarning().c_str()); } } ; @@ -1353,6 +1353,19 @@ attr: { $$ = new Attr(ATTR_ERROR_HANDLER); } | TOK_ATTR_DEPRECATED { $$ = new Attr(ATTR_DEPRECATED); } + | TOK_ATTR_DEPRECATED '=' TOK_CONSTANT + { + if ( IsString($3->Type()->Tag()) ) + $$ = new Attr(ATTR_DEPRECATED, new ConstExpr($3)); + else + { + ODesc d; + $3->Describe(&d); + reporter->Error("'&deprecated=%s' must use a string literal", + d.Description()); + $$ = new Attr(ATTR_DEPRECATED); + } + } ; stmt: @@ -1561,7 +1574,7 @@ event: YYERROR; } if ( id->IsDeprecated() ) - reporter->Warning("deprecated (%s)", id->Name()); + reporter->Warning("%s", id->GetDeprecationWarning().c_str()); } $$ = new EventExpr($1, $3); @@ -1767,7 +1780,7 @@ global_or_event_id: if ( t->Tag() != TYPE_FUNC || t->AsFuncType()->Flavor() != FUNC_FLAVOR_FUNCTION ) - reporter->Warning("deprecated (%s)", $$->Name()); + reporter->Warning("%s", $$->GetDeprecationWarning().c_str()); } delete [] $1; @@ -1813,9 +1826,23 @@ opt_no_test_block: opt_deprecated: TOK_ATTR_DEPRECATED - { $$ = true; } + { $$ = new ConstExpr(new StringVal("")); } | - { $$ = false; } + TOK_ATTR_DEPRECATED '=' TOK_CONSTANT + { + if ( IsString($3->Type()->Tag()) ) + $$ = new ConstExpr($3); + else + { + ODesc d; + $3->Describe(&d); + reporter->Error("'&deprecated=%s' must use a string literal", + d.Description()); + $$ = new ConstExpr(new StringVal("")); + } + } + | + { $$ = nullptr; } %% diff --git a/src/plugin/ComponentManager.h b/src/plugin/ComponentManager.h index 399c704551..30b3628588 100644 --- a/src/plugin/ComponentManager.h +++ b/src/plugin/ComponentManager.h @@ -244,7 +244,7 @@ void ComponentManager::RegisterComponent(C* component, string id = fmt("%s%s", prefix.c_str(), cname.c_str()); tag_enum_type->AddName(module, id.c_str(), component->Tag().AsEnumVal()->InternalInt(), true, - false); + nullptr); } } // namespace plugin diff --git a/testing/btest/Baseline/language.deprecated/no-warnings.out b/testing/btest/Baseline/language.deprecated/no-warnings.out new file mode 100644 index 0000000000..42930b1690 --- /dev/null +++ b/testing/btest/Baseline/language.deprecated/no-warnings.out @@ -0,0 +1,28 @@ +warning in ./no-warnings.zeek, line 27: deprecated (ONE) +warning in ./no-warnings.zeek, line 28: deprecated (TWO) +warning in ./no-warnings.zeek, line 30: deprecated (GREEN) +warning in ./no-warnings.zeek, line 31: deprecated (BLUE) +warning in ./no-warnings.zeek, line 33: deprecated (blah) +warning in ./no-warnings.zeek, line 37: deprecated (my_event) +warning in ./no-warnings.zeek, line 38: deprecated (my_event) +warning in ./no-warnings.zeek, line 39: deprecated (my_hook) +warning in ./no-warnings.zeek, line 41: deprecated (my_record$b) +warning in ./no-warnings.zeek, line 42: deprecated (my_record$b) +warning in ./no-warnings.zeek, line 43: deprecated (my_record$b) +warning in ./no-warnings.zeek, line 45: deprecated (my_record?$b) +warning in ./no-warnings.zeek, line 46: deprecated (my_record$b) +warning in ./no-warnings.zeek, line 49: deprecated (my_record$b) +warning in ./no-warnings.zeek, line 52: deprecated (my_event) +warning in ./no-warnings.zeek, line 57: deprecated (my_hook) +warning in ./no-warnings.zeek, line 62: deprecated (blah) +warning in ./no-warnings.zeek, line 71: deprecated (dont_use_me) +warning in ./no-warnings.zeek, line 76: deprecated (dont_use_me_either) +ZERO +ONE +TWO +RED +GREEN +BLUE +generate my_hook please +generate my_event please +schedule my_event please diff --git a/testing/btest/Baseline/language.deprecated/out b/testing/btest/Baseline/language.deprecated/out deleted file mode 100644 index 3126b1e78b..0000000000 --- a/testing/btest/Baseline/language.deprecated/out +++ /dev/null @@ -1,28 +0,0 @@ -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 30: deprecated (ONE) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 31: deprecated (TWO) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 33: deprecated (GREEN) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 34: deprecated (BLUE) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 36: deprecated (blah) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 40: deprecated (my_event) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 41: deprecated (my_event) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 42: deprecated (my_hook) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 44: deprecated (my_record$b) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 45: deprecated (my_record$b) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 46: deprecated (my_record$b) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 48: deprecated (my_record?$b) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 49: deprecated (my_record$b) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 52: deprecated (my_record$b) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 55: deprecated (my_event) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 60: deprecated (my_hook) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 65: deprecated (blah) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 74: deprecated (dont_use_me) -warning in /Users/jsiwek/Projects/bro/bro/testing/btest/.tmp/language.deprecated/deprecated.zeek, line 79: deprecated (dont_use_me_either) -ZERO -ONE -TWO -RED -GREEN -BLUE -generate my_hook please -generate my_event please -schedule my_event please diff --git a/testing/btest/Baseline/language.deprecated/warnings.out b/testing/btest/Baseline/language.deprecated/warnings.out new file mode 100644 index 0000000000..5f0ee7bdc8 --- /dev/null +++ b/testing/btest/Baseline/language.deprecated/warnings.out @@ -0,0 +1,28 @@ +warning in ./warnings.zeek, line 27: deprecated (ONE): one warning +warning in ./warnings.zeek, line 28: deprecated (TWO): two warning +warning in ./warnings.zeek, line 30: deprecated (GREEN): green warning +warning in ./warnings.zeek, line 31: deprecated (BLUE): red warning +warning in ./warnings.zeek, line 33: deprecated (blah): type warning +warning in ./warnings.zeek, line 37: deprecated (my_event): event warning +warning in ./warnings.zeek, line 38: deprecated (my_event): event warning +warning in ./warnings.zeek, line 39: deprecated (my_hook): hook warning +warning in ./warnings.zeek, line 41: deprecated (my_record$b): record warning +warning in ./warnings.zeek, line 42: deprecated (my_record$b): record warning +warning in ./warnings.zeek, line 43: deprecated (my_record$b): record warning +warning in ./warnings.zeek, line 45: deprecated (my_record?$b): record warning +warning in ./warnings.zeek, line 46: deprecated (my_record$b): record warning +warning in ./warnings.zeek, line 49: deprecated (my_record$b): record warning +warning in ./warnings.zeek, line 52: deprecated (my_event): event warning +warning in ./warnings.zeek, line 57: deprecated (my_hook): hook warning +warning in ./warnings.zeek, line 62: deprecated (blah): type warning +warning in ./warnings.zeek, line 71: deprecated (dont_use_me): global function warning +warning in ./warnings.zeek, line 76: deprecated (dont_use_me_either): function warning +ZERO +ONE +TWO +RED +GREEN +BLUE +generate my_hook please +generate my_event please +schedule my_event please diff --git a/testing/btest/language/deprecated.zeek b/testing/btest/language/deprecated.zeek index 6e10d7d744..b10b5674d3 100644 --- a/testing/btest/language/deprecated.zeek +++ b/testing/btest/language/deprecated.zeek @@ -1,6 +1,10 @@ -# @TEST-EXEC: zeek -b %INPUT >out 2>&1 -# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff out +# @TEST-EXEC: zeek -b no-warnings.zeek >no-warnings.out 2>&1 +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff no-warnings.out +# @TEST-EXEC: zeek -b warnings.zeek >warnings.out 2>&1 +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff warnings.out + +@TEST-START-FILE no-warnings.zeek type blah: string &deprecated; global my_event: event(arg: string) &deprecated; @@ -21,7 +25,7 @@ type my_enum: enum { type my_other_enum: enum { ZERO = 0, ONE = 1 &deprecated, - TWO = 2 &deprecated + TWO = 2 &deprecated, }; event zeek_init() @@ -78,3 +82,84 @@ function dont_use_me_either() &deprecated { dont_use_me_either(); } +@TEST-END-FILE + +@TEST-START-FILE warnings.zeek +type blah: string &deprecated="type warning"; + +global my_event: event(arg: string) &deprecated="event warning"; + +global my_hook: hook(arg: string) &deprecated="hook warning"; + +type my_record: record { + a: count &default = 1; + b: string &optional &deprecated="record warning"; +}; + +type my_enum: enum { + RED, + GREEN &deprecated="green warning", + BLUE &deprecated="red warning" +}; + +type my_other_enum: enum { + ZERO = 0, + ONE = 1 &deprecated="one warning", + TWO = 2 &deprecated="two warning", +}; + +event zeek_init() + { + print ZERO; + print ONE; + print TWO; + print RED; + print GREEN; + print BLUE; + + local l: blah = "testing"; + + local ls: string = " test"; + + event my_event("generate my_event please"); + schedule 1sec { my_event("schedule my_event please") }; + hook my_hook("generate my_hook please"); + + local mr = my_record($a = 3, $b = "yeah"); + mr = [$a = 4, $b = "ye"]; + mr = record($a = 5, $b = "y"); + + if ( ! mr?$b ) + mr$b = "nooooooo"; + + mr$a = 2; + mr$b = "noooo"; + } + +event my_event(arg: string) + { + print arg; + } + +hook my_hook(arg: string) + { + print arg; + } + +function hmm(b: blah) + { + print b; + } + +global dont_use_me: function() &deprecated="global function warning"; + +function dont_use_me() + { + dont_use_me(); + } + +function dont_use_me_either() &deprecated="function warning" + { + dont_use_me_either(); + } +@TEST-END-FILE