-------------------------------------------------- --- Manual Additions/Modifications --- -------------------------------------------------- PC-lint for C/C++ Version 9.00i This readme.txt supplements the PC-lint manual entitled "Reference Manual for PC-lint/FlexeLint" found in the installation directory under the name "pc-lint.pdf" ------ Printing the Reference Manual ------ You have permission to print out the Reference Manual (or other related documentation) in whole or in part in support of the use of this software. ------ Support for Microsoft through Visual Studio 2010 ------ Patches 9.00e and later support Visual Studio 2010 and the Microsoft Visual C/C++ 10.0 compiler (as well as all earlier versions of the Microsoft compiler series). Support comes mainly from the compiler options files (co-...lnt). Thus options files co-msc70.lnt, co-msc71.lnt, co-msc80.lnt, co-msc90.lnt and co-msc100.lnt support versions 7.0, 7.1, 8.0, 9.0 and 10.0 respectively. The file co-msc100.lnt is preconfigured to match the default macro settings of the compiler. However, owing to compiler options the settings of these macros are subject to change. We have therefore provided a way to automatically generate the macro definitions. See co-msc100.lnt and/or vs2010-m.cpp. Also, starting with 9.00e, PC-lint supports .sln and .vcxproj files as described below under ".sln Support" and ".vcxproj Support" respectively. ------ What's New in Version 9 ------ To find out what we've added to the product since Version 8.00, check out Chapter 20 "What's New" in the Reference Manual. We traditionally include Version Shock in our "What's New" section. This was inadvertently omitted in producing the document. So here it is. ------ Ongoing support and patch versions. Periodically between versions we have so-called patches. These are intended to fix bugs and provide support for new technologies (language versions, compiler features, etc.). A feature-by-feature list is presented on our web site www.gimpel.com. Click on "Lint 9.00 Patches" and look at bugfix90.txt ------ Version Shock wchar_t -- We are treating wchar_t slightly differently in Version 9. The difference would not normally be detected. See flag -fwc in Section 5.5 of the manual. Strong Type Change -- We now support dimensional analysis using strong types. This resulted in some changes which are not upward compatible. See Section 9.4 "Multiplication and Division of Strong Types". You can opt out of this change. See The Dimension by Default flag (-fdd) in Section 5.5 and Jm controls the Multiplier group flag (-fjm) also in Section 5.5. ------ Front End ------ Your linting experience will be considerably enhanced by adapting your favorite editor or compiler environment to the task of sequencing from error to error. See Section 3.5 of the Reference Manual. ------ Getting Started ------ In section 3.2 of our Reference Manual we indicate that you need to specify where your compiler header files are located as well as the location of any third party libraries you might be using. This is done using one or more -i options. If you are not aware of where your libraries are you can compile (using your compiler, not lint) the file called: where_is_stdio_h.c The purpose and use of the file is documented as commentary within the file itself. It is designed to find the location of your compiler's headers and contains instructions on locating other library headers as well. ------ Multiple Passes ------ By default, PC-lint/FlexeLint will go through all your modules in one pass. For projects not previously linted there will be enough messages to look at. However, with just one pass, you will not be taking full advantage of our new static data tracking or the interfunction value tracking. With just one pass, we cannot say very much about static variables. We may see a variable being set to a value but we don't know of intervening functions that may be modifying the variable. We will also not know about dangerous return values for functions that are defined later than they are called, and we will not know about dangerous arguments for functions that are defined early. To introduce a second pass you need only to add the command line option: -passes(2) or, if this syntax presents a problem with your Shell, you may use: -passes[2] or, in some cases, -passes=2 is needed. Of course, you can replace the 2 with any number you wish. The larger the number, the more bugs that can be found and the more processing time that will be required. See Section 10.2 "Value Tracking" in the Reference Manual. ------ What's New in the 9.00b Patch ------ We have been diligent in responding to users that may have experienced difficulties in one of the many new options that Version 9.00 provides. These features include Pre-compiled Headers (-pch option), Bypass Headers (+/-byph(), etc.) and pre-determined predicates. ------ What's New in the 9.00c Patch ------ We have special author files ('au-' prefix) for porting to 64-bit programming. The names of these files are perhaps self-explanatory: au-LLP64.lnt LLP64 model au-LP64.lnt LP64 model au-ILP64.lnt ILP64 model Each of these files references the model-independent file au-64.lnt that enables Warnings, Informationals and Elective Notes suitable when porting from 32 bits to 64 bits. The file also contains directions for employing strong typing to assist in torture testing your code. In attempting to track the forthcoming C++ standard we have implementations of a number of voted on features. These are available using the option: -A(C++2010) and consist of: static_assert() Extended friend Declarations Adding the long long Type to C++ C99 Preprocessor Synchronization Right Angle Brackets 'extern template' Delegating Constructors Using 'auto' to deduce types of variables Rvalue References New Character Types sizeof() for non-static members without 'this->' decltype Strongly Typed Enums A name for the null pointer: nullptr ------ What's New in the 9.00d Patch ------ In addition to the usual set of miscellaneous bug fixes we have made a substantial upgrade to our overload resolution algorithms to accommodate some subtle language requirements. Some additions have been made to our suite of C++ 0x features (activated through the use of the option: -A(C++2010)) and consist of: 'explicit' conversion functions explicitly defaulted special member functions An important bug was found and fixed in connection with pre-compiled headers and MSVC 8.0 and 9.0 ------ What's New in the 9.00e Patch ------ Visual Studio 2010 is now supported. Our MISRA support (MISRA C 1998, MISRA C 2004 and MISRA C++) has been substantially enhanced and our coverage of the various rules is now virtually complete. See Author files au-misra1.lnt, au-misra2.lnt and au-misra-cpp.lnt. Improved emulation of the Microsoft header search algorithm. See "Microsoft's nested #include search" below. Improved support for embedded systems using the @ location specifier. See "Enhanced support for address-specifiers" below. Improved Boost.Typeof support for Visual Studio users owing to better emulation of Microsoft's __if_exists keyword. ------ What's New in the 9.00f Patch ------ 9.00f removes a troubling undeserved 1013 introduced in 9.00e. A number of MISRA issues were addressed. A template bug was removed thereby helping us greatly with our Boost lambda support. ------ What's New in the 9.00g Patch ------ Patch 9.00g introduces support for lambda-expressions and alias declarations. As with other C++0x features, these are available using the option: -A(C++2011). The complete list of supported core language proposals is as follows. (Where given, the tags in square brackets indicate the names of relevant sections in the Final Draft International Standard (FDIS).) static_assert declarations [dcl.dcl] Extended friend declarations The type 'long long' C99 preprocessor synchronization Right angle brackets [temp.names] 'extern template' [temp.explicit] Delegating constructors [class.base.init] The 'auto' type specifier [dcl.spec.auto] Rvalue references New Character Types sizeof() for non-static members without 'this->' decltype [dcl.type.simple] Strongly Typed Enums [dcl.enum] 'nullptr' 'explicit' conversion functions Defaulted functions [dcl.fct.def.default] Deleted functions [dcl.fct.def.delete] Explicitly defaulted special member functions Lambda expressions [expr.prim.lambda] Alias declarations [dcl.typedef] Other Enhancements include: Our .lnt files now contain an embedded date-last-changed identification which can be reflected to the output stream (check any .lnt file). We have continued to enhance our MISRA C and MISRA C++ checking. We also now support the simultaneous activation of both MISRA C 1998 and MISRA C 2004 thru the use of the combination of the +misra(1) and +misra(2) options. We have improved and extended our processing with respect to standard limits. We now avoid issuing warnings #417 and #694 prematurely when processing macro expansions. We now remain silent with respect to elective note #952 for parameters declared as arrays. We corrected a crash which occurred when an EOF was found inside a macro argument list. Though processing will still stop in such cases, we now recover much more gracefully. We addressed the occurrence of a segmentation fault when attempting to use the program info facility. We have harmonized Lint's behavior with respect to ++efreeze and message activation options to allow such options to take effect, as described in the manual. We were inadvertently attempting to define a function after the end of a module was found, resulting in a segmentation fault. We have taken steps to prevent this behavior. We were issuing a segmentation fault over the processing of '__declspec'. This error has been corrected. Plus numerous other minor and not-so-minor corrections. Disenhancements: Regrettably we are no longer able to upgrade our MSDOS version of PC-lint. It has been frozen at 9.00f. Note this does not affect our Windows command-line version, lint-nt.exe, nor our OS/2 version, lint-os2.exe. ------ What's New in the 9.00h Patch ------ Version 9.00g introduced initial support for C++0x Lambda-expressions; this initial version of Lambda support introduced a regression in our support for attribute syntax in the C++/CLI dialect. As a result some Microsoft users may have seen undesired error messages like the following. #... [SA_Pre(Null=SA_No)] #... _Pre1_impl_(_$notnull) _Deref_pre1_impl_(_$valid) #... _Pre_valid_ _Post_valid_ #... _Prepost_valid_ inline HRESULT CComVariant::WriteToStream(_Inout_ IStream* pStream) c:\vs9\VC\atlmfc\include\atlcomcli.h(2637): error 26: (Error -- Expected an expression, found '[') This is now resolved by an improved disambiguator. * Template alias support as described in C++ 0x documentation * Support for the __asm keyword semantics as understood by ARMcc and WindRiver. The new keyword is __ARM_asm and can be used to set the semantics of __asm by using the option -rw_asgn(__asm,__ARM_asm) * Alignment of an empty class is now assumed to be 1 (rather than 0) * It contains our latest efforts at understanding and implementing the mysteries of when extern "C" meets with static. * We now perform a more accurate analysis for compliance with MISRA C 2004, rule #8.7. * We were tripping over a printf-style format specifier which involved a string literal and the binary '+' operator. We now handle this situation more gracefully. * We were accidentally describing two structures with different definitions but identical tags as being defined identically. We now recognize the difference. * We now refrain from issuing elective note #953 if the object is a variable with class or struct type and lacks a User-provided default constructor. * We now allow catch parameters to be inheritance related to the type thrown. * We now allow physical tabs to appear in the output by use of \T * We now flag uses of 00 and \00 within the MISRA prohibition of the use of octal. We still allow single 0's. * We no longer suggest that block-scope using declarations might be declared as const. * We now avoid duplicate appended text for the same message number. ------ What's New in the 9.00i Patch ------ * We introduced support for UTF-16 byte encoding (both Big and Little Endian). This adds to our support for UTF-8 and plain Ascii. * We extended our support for .vcxproj files by digging out include and pre-defined symbols appropriate to a build. * Introduced initial support for GCC attributes. Unless otherwise documented, GCC attributes will be ignored. Over time, we will implement support for specific attributes as demand and resources warrant. To err on the side of caution, we do not enable "__attribute__" as a reserved word by default. (Non-GCC compilers may use that spelling for a different form of attribute syntax.) Instead, we have endowed the semantics of GCC's __attribute__ in a reserved word that is spelled "__gcc_attribute__" and disabled by default. Support can be enabled with an option like this: -rw_asgn(__attribute__,__gcc_attribute__) * Introduced support for the GCC "mode" attribute * We now properly interpret the following loop int a[10]; for( i = 10; i > 0; i-- ) a[i] = 0; and give the expected warning (661 out-of-bounds pointer) fixing a bug that was introduced in Vers. 8.00x. But bewarned that the lower bound tracking for down-progressing indices is thereby compromised. A complete fix will be available at some later time. * We now support braceless switch statements; previously they were given an Error notification. The reason for the change was the realization that "switch(0) case 0:" can be a useful introductory clause to a compound statement from which one can use the break statement to break out of. * We are continuing the effort to improve our support for Misra, adding messages for MISRA C 1998 #62, MISRA C 2004 #15.3, and MISRA C++ #6-4-6, and #6-2-1. * Numerous bugs have been fixed. For a complete list, go to our web site www.gimpel.com and click on "Lint 9.00 Patches". And then find file "bugfix90.txt". ------ Additional Options ------ The following features have been added since our document was produced. o Turning off +source You may now turn off the +source option using the option --source. o Arguments to compiler code options Compiler code options (which have the form -ccode) may in some cases now take arguments. The arguments will typically reflect compile options passed to a compiler; thus they will tend to be vendor-specific. The supported option and argument is: -cmsc( clr ) This not only sets the compiler to the Microsoft compiler but also indicates that the project's code is compiled with the /clr option, which enables support for certain core-language extensions. When the above Lint option is given, Lint will attempt to gracefully ignore such extensions. (Without -cmsc(clr), Lint will generally assume Standard-conforming code and issue errors where some CLR extensions are used.) o .vcxproj support A .vcxproj file is a recent addition to Microsoft's Visual Studio. Like its cousin the .vcproj file, a .vcxproj file describes a single project, possibly consisting of multiple modules. We now process such files in a manner similar to .vcproj files (as described in the manual). Thus, if you have a file named x.vcxproj you may process it as follows: lint-nt x.vcxproj >x.lnt This will capture in x.lnt, the module names that are embedded in x.vcxproj. Unlike .vcproj files, we do not (yet) attempt to deduce either -i options or -d options. o .sln support A file whose extension is .sln is treated by the Microsoft Visual Studio as a solution file. It will contain the names of one or more project files. These can now be processed by PC-lint. For example if you have a file named s.sln it can be processed with the following command: lint-nt s.sln >s.bat The output (s.bat) will contain a sequence of lint commands, one command for each .vcproj or .vcxproj name contained within the .sln file. Each command is similar to the one shown above for x.vcxproj. The curious reader may wonder why we create an intermediate file rather than simply process the entire solution file. The reason is that a .sln file contains information about multiple projects and a single run of PC-lint is incapable of handling more than one project. We can handle multiple modules of the same project but not multiple projects. The list of commands that emerge from the .sln file represents independent runs of PC-lint. o Enhanced support for address-specifiers As indicated in Section 5.8.3 Case '@' of the PC-lint/FlexeLint manual, some compilers that target embedded systems provide a language extension that enables the user to specify, in a declaration of a statically-allocated variable or a function, an address where the declared entity is to be stored. Example: int a @ 0xFF02; // 'a' is at memory location 0xff02 Since Version 9.00e we are treating the '@' location not as an initializer but as a separate address specifier. This corrected two problems; we were not able to use a location specifier in a forward declaration and initializers could not accompany location specifiers. Thus we may now write int b @ 0xFF00 = 42; // initializes b, at 0xff00, to 42 So the grammar for /init-declarator/ is: init-declarator: declarator [address-specifier] [initializer] address-specifier: '@' constant-expression This is now reflected in our parser. o Microsoft's nested #include search This information may be considered as an addendum to Section 15.2 Include Processing, Case 1. As of Version 9.00e the option +compiler(search_actively_including_stack) has been added to the compiler options file for the Microsoft compilers co-msc70.lnt through co-msc90.lnt. This option has the effect of emulating Microsoft's "nested #include search" feature, in which an include-directive of the form: #include "a.h" causes the search for "a.h" to consider first the directory of the including file and then the directories of all other actively-including files (from most nested all the way through the directory containing the primary source file) before considering directories specified by '-i' options. The option has the side effect of setting +fdi. ------ New or Improved Error Messages ------ 165 An [unscoped] enumeration cannot be forward-declared [without an enum-base] (int is assumed) -- This message is issued at the point of a forward-declaration of an enumeration like so: enum E; // Error This is prohibited by ISO C and ISO C++98. In C++0x, we can modify this example to be well-formed by explicitly indicating the underlying integral type; example: enum E : unsigned short; // Ok If you are not using C++0x and/or your compiler supports the construct you may simply suppress this message with a -e165. 166 Function defined within a function -- A function definition was found within the body of another function's definition. Such a construct is almost certainly an error. 318 EOF for a module found within a macro argument list -- We found the end of a module within the argument list of a macro. Since such situations are almost certain to be erroneous, we gracefully shut down, alerting the User to the reason. 510 File extension 'String' reserved for future versions of this product -- File name extensions that are not those recognized as implying C++ source code or indirect files for lint or pre-compiled headers for lint or lint object modules or project files are assumed to be C source code. If we recognize a new file extension in some future version of lint it can be beneficial to warn about the use of this file extension in any earlier version of lint. One reason for this is to aid in the transition between versions of the product. During this transition period a new file extension may be provided unintentionally to a former version of the product resulting in surprising behavior. 1009 operator 'String' not redefinable -- The three operators: .* ? . are not redefinable and may not be overloaded [11, 13.4]. 1084 Ambiguous use of template-id for instantiation of 'Type' -- When the language calls for a class template to be instantiated and the primary template is "overloaded" via one or more partial specializations, there is an attempt to see if the template arguments match any of those partial specializations. (Note, explicit specializations would have been considered before determining that the class definition needs to be generated by way of instantiation.) If multiple partial specializations match then: - If one of the matching partial specializations is more specialized than all others then it is used for the instantiation. - Otherwise, the program is ill-formed, so Lint issues message 1084. In the message, the matching partial specializations are provided as the list of candidates. Example: template class A {}; //#1 template class A {}; //#2 template class A {}; //#3 A a; // ambiguous: matches #2 and #3 // (and neither template is more specialized than the other) 1096 A target ctor must be the only mem-initializer in the mem-initializer-list of a delegating ctor -- C++0x requires that if a constructor delegates to another constructor, then the mem-initializer (the region between the colon and the function body) must contain only one item, and that item must be a call to another constructor (which is called the "target constructor"). Example: struct A { int n; A(int); A( const A& p) : A(p.n) {} // Ok A() : n(42), A(32) // Error 1096 {} }; 1097 Delegating ctor delegates directly to itself, causing infinite recursion -- Example: struct A { int n; A(int x) : A(x){} // Error 1097 }; 1098 Function template specialization 'Symbol' does not match any function template -- This message is issued for a declaration where the user apparently intended to name a specialization of a function template (e.g., in an explicit specialization, an explicit instantiation or a friend declaration of specialization), but no previously-declared function template is matched. Example: template void f( const T& ); // #1 struct A{}; template<> void f( const A& ); // Ok // (A is the deduced argument to T.) struct B{}; template<> void f( const B ); // Error 1097. // (A template argument cannot be deduced for T.) 1099 Ambiguous function template specialization 'Symbol' -- This message is issued for a declaration where the user apparently intended to name a specialization of a function template (e.g., in an explicit specialization, an explicit instantiation or a friend declaration of specialization), but the specialization matches multiple function templates, and none of the matched templates is more specialized than all of the other matching templates. The candidates (i.e., the matching templates) are provided in the message. Example: template struct A {}; template void f( T*, U ); // #1 template void f( T, A ); // #2 struct B{}; template<> void f( B, A ); // Ok // #1 does not match but #2 does. template<> void f( char*, A ); // Error 1099 // Both #1 and #2 match and neither is more specialized than the // other. This situation can be avoided in at least a couple of ways. One way is to explicitly specify one or more template arguments. Example: // continuing from above... template<> void f( char*, A ); // Ok // #1 does not match but #2 does. Another way is to use SFINAE tactics in the declaration of one or more function templates, e.g. with boost::enable_if. 1100 Declaration of 'Symbol' does not declare an explicit specialization, explicit instantiation or friend -- In a declaration that explicitly specifies template arguments with angle brackets immediately after the name of a function template, the declaration must declare either an explicit specialization, explicit instantiation or friend. (Note, an explicit specialization always begins with 'template<>' and an explicit instantiation always begins with 'template'---without angle brackets after the keyword 'template'.) template struct A {}; template inline void f( A ); // #1 void f( A ); // #2 // Ok, declares an ordinary function void f( A ); // Error 1100 1101 Type of variable 'Symbol' cannot be deduced from its initializer -- Example: int f(void); int f(char*); auto n = f; // Error In terms of deduction, this is equivalent to: int f(void); int f(char*); template void g( const T& ); void h( void ) { g( f ); // Error } Here, 'f' refers to multiple overloaded functions, so it is an ambiguous reference and T cannot be deduced. (Code like this could still be well-formed however, e.g. if g is overloaded with a non-template function whose parameter type is 'ptr-to-function returning int taking (char*)'.) 1102 auto type deduced inconsistently: 'Type' for 'Symbol' but 'Type' for 'Symbol' -- When multiple variables are defined in the same declaration, and when that declaration uses the keyword auto as the type-specifier (a feature of C++0x), the type for which auto is a placeholder must be the same for each variable. Example: float g(void); char* s(); auto a = 42; // Ok, auto is 'int' auto b = g(); // Ok, auto is 'float' auto c = 'q', *d = s(); // Ok, auto is 'char' (for both c and d) auto x = 42, y = g(); // Error 1102 here 1103 Type 'Type' is not allowed as an enum-base -- When an enumeration type is declared with an explicit underlying type, that type must be integral. Example: enum A : bool; // ok enum B : short; // ok enum C : unsigned long long; // ok enum D : float; // Error 1103 1104 A reference to enumeration 'Symbol' should not use 'String' -- Although an enumeration may be declared or defined using a scope indicator or an underlying type indicator, these should not be applied when simply referencing the enumeration. E.g. enum class A { red, green }; enum class A x; // Error: don't need 'class' enum A : unsigned { red, green }; enum A : unsigned y; // Error: don't need ': unsigned' 1105 Use of ref qualification of 'Symbol' inconsistent with overloaded function 'Symbol' (Location) -- If an explicit ref qualifier ('&' or '&&') of a nonstatic member function is employed, an explicit ref qualifier needs to be used with every member of the overload set. Thus: class A { void f(int) &; // ok (so far) void f(int); // 1105 void f(double); // 1105 void g(int); // ok (fresh function) void g(double); // still ok }; 1106 Initializing value 'String' of enumerator 'Name' cannot be represented by the enumeration's underlying type 'Type' -- An enumerator is being initialized with a value that is inappropriate to the declared type of the initializer. Example: enum E : unsigned char { e = 256 }; The value 256 cannot be represented by an unsigned char. 1107 Mixing two different kinds of string literals -- Two string literals are being concatenated which have different types. Examples: char *s = u"abc" U"def"; char *q = L"ghi" u"jkl"; This message is issued for mixing strings of char16_t, char32_t, and/or wchar_t (as shown). Literal string concatenation of any of these with an ordinary character literal is permitted and will receive Informational 707. 1108 Use of deleted function 'Symbol' defined at 'Location' -- This message is issued when a deleted function is used. Example: void f( int ) = delete; void f( double ); void g( double d, int n ) { f( d ); // Ok f( n ); // Error } 1110 Cycle detected: explicit application of 'Name'::operator-> causes infinite implicit applications of the same operator -- When an overloaded operator-> is used as in a->b it is effectively expanded to: a.operator->()->b And this expansion repeats until an operator-> is found that does not yield a class type. But in the process of evaluating this expansion, it might be found that one of the operators returns a class type for which an overloaded operator-> was already expanded; in that case, Error 1110 is triggered. Example: struct B; struct A { struct B& operator->(); }; struct B { struct A& operator->(); }; int f( A & p ) { p->g(); } // Error 1111 ISO C++ requires an explicit specialization/instantiation to appear at namespace scope -- This message is issued at the beginning of each explicit specialization/instantiation that does not appear at namespace scope. Example: struct A { template struct B {}; // template <> // Would be ill-formed by ISO C++. // struct B {}; }; template<> struct A::B {}; // Ok. There is an additional limitation with member class templates of class templates. As with members of a non-template class, one cannot write a specialization at class scope. Example: template struct G { template struct H {}; // template <> // Would be ill-formed by ISO C++. // struct H {}; }; But the language specification does not even allow this to be expressed in a namespace-scope definition; there is no way to write an explicit specialization that is a member of a class template. Example: template struct J { template struct K {}; }; // template // template <> // Would be ill-formed by ISO C++; // struct J::K {}; This is because the rules for explicit specializations say that 'template<>' is not allowed to appear after a non-empty template-parameter-list within the same declaration. However, one may write an explicit specialization that is a member of an implicitly-instantiated specialization of a class template. Example: template struct L { template struct M {}; }; template <> template <> struct L::M {}; // Ok Here, the body of the class L is automatically generated by implicit instantiation (otherwise the reference to 'L::M' would be ill-formed), while the body of L::M is provided in the explicit specialization. In March of 2009, the ISO C++ committee reviewed a report submitted against this example: struct A { template struct B; template struct B { }; // well-formed template <> struct B { }; // ill-formed }; While it might seem odd that one is able to write the partial specialization but not the full specialization, the committee (which at the time was in a "feature-freeze" mode and trying to finalize a draft for the next International Standard) decided that this capability would need to be regarded as an "extension", meaning that it could be considered as a new feature in a future standard but not as a bug-fix for C++0x. Note that the Microsoft compiler implements this extension. For that reason, the Lint option -elib(1111) appears in recent versions of our configuration files for Microsoft compilers. 1574 Returning the address of an auto variable indirectly through reference variable 'Symbol' -- Within a function whose return type is reference to some type, a return statement is returning a reference which has been initialized (possibly indirectly) with an auto variable. For example: int &f( int k ) { int &r = k; return r; } Gimpel Software January, 2012