home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / lifeos2.zip / LIFE-1.02 / TESTS / FILES / TESTSLC.RUL < prev    next >
Text File  |  1996-06-04  |  18KB  |  600 lines

  1. %
  2. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  3. %
  4. %
  5. %                    A SMALL SET OF RULES FOR SUPERLINT
  6. %                    ----------------------------------
  7. %
  8. %  
  9. %
  10. %  Last modification : March 4th 1994 
  11. %
  12. %
  13. %
  14. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  15. %
  16. %
  17. %  (C) Digital Equipment Corporation 1993 - 1994  
  18. %
  19. %
  20. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  21. %
  22.  
  23.  
  24.  
  25. %
  26. % ------------------------------------------------------------------------------
  27. %
  28. % The rule 'variable_length" applies a metric test to the identifiers declared 
  29. % as local variables in a block : such an identifier must have a length greater
  30. % than or equal to the square root of the size of the block (in lines) in which 
  31. % it is defined. This rule insures that local variables of long blocks will have
  32. % long enough and wishfully explicit enough names.
  33. % This is one of the first rules proposed by Bill McKeeman who initiated the
  34. % SuperLint project.
  35. %
  36. % ------------------------------------------------------------------------------
  37. %
  38.  
  39. variable_length(Declaration, declaration) :->
  40. {
  41.   entry_test(parse_mode == prefix
  42.              && Declaration == local_declaration),
  43.   Block = Declaration.scope,
  44.   LeftBrace = Block.left_brace,
  45.   RightBrace = Block.right_brace,
  46.   if(LeftBrace.file <> RightBrace.file) then
  47.     error_msg("Braces around a block must be in the same file",
  48.           RightBrace)
  49.   else {
  50.     if(strlen(Declaration.name.id) =< sqrt(RightBrace.line - LeftBrace.line)) then
  51.       error_msg("Variable name is too short", Declaration.name)
  52.   }
  53. }.
  54.  
  55.  
  56. %
  57. % ------------------------------------------------------------------------------
  58. %
  59. % The rule 'extern_in_c_file' insures that there is no extern declaration in
  60. % a '.c' file. Such declarations should only figure in '.h' files.
  61. %
  62. % ------------------------------------------------------------------------------
  63. %
  64.  
  65. extern_in_c_file(Declaration, declaration) :->
  66. {
  67.   entry_test(parse_mode == prefix
  68.              && Declaration == external_declaration),
  69.   if(Declaration.type.store_class == extern
  70.      && file_extension(Declaration.name.file) == "c") then
  71.     error_msg("No declaration in a .c file accessed out of the .c file",
  72.           Declaration.name)
  73. }.
  74.  
  75.  
  76. %
  77. % ------------------------------------------------------------------------------
  78. %
  79. % The rule 'block_alignment' tests for the indentation of 'if', 'while' and
  80. % 'for' statements when bodies of these instructions are blocks.
  81. % The open brace must be separated by one blank space from the closing
  82. % parenthesis of the condition (or from the 'else' keyword) and the
  83. % closing brace must be on the same row that the corresponding keyword ('if',
  84. % 'else', 'while' or 'for').
  85. % It uses an auxiliary procedure : 'test_alignment'.
  86. %
  87. % ------------------------------------------------------------------------------
  88. %
  89.  
  90. block_alignment(Instruction, alignment) :->
  91. {
  92.   entry_test(parse_mode == prefix
  93.              && is_not_token(Instruction)
  94.              && (
  95.                  Instruction == (if)
  96.                  || Instruction == while
  97.                  || Instruction == for
  98.                 )
  99.              ),
  100.   switch(Instruction, 
  101.     {
  102.       case({while, for},
  103.         {
  104.           KeyWord = Instruction.keyword,
  105.       Body = Instruction.body,
  106.       if(Body == block) then
  107.             test_alignment(KeyWord, Body)
  108.     }
  109.       ),
  110.       case((if), 
  111.         {
  112.       IfKeyWord = Instruction.keyword,
  113.       ThenBody = Instruction.then_body,
  114.       if(ThenBody == block) then
  115.         test_alignment(IfKeyWord, ThenBody),
  116.           ElseBody = Instruction.else_body,
  117.           if(ElseBody <> nothing) then {
  118.         ElseKeyWord = Instruction.else_keyword,
  119.             if(ElseBody == block) then
  120.           test_alignment(ElseKeyWord, ElseBody)
  121.           }
  122.         }
  123.       )
  124.     }
  125.   )
  126. }.
  127.  
  128.  
  129. procedure test_alignment(KeyWord, Block) :->
  130. {
  131.   LeftBrace = Block.left_brace,
  132.   RightBrace = Block.right_brace,
  133.   if(LeftBrace.white_spaces <> " ") then
  134.     error_msg("There must be one space between brace and condition",
  135.           LeftBrace),
  136.   if(RightBrace.column <> KeyWord.column) then
  137.     error_msg("Brace must be aligned with first character of instruction",
  138.               RightBrace)
  139. }.
  140.  
  141.  
  142. %
  143. % ------------------------------------------------------------------------------
  144. %
  145. % The rule 'nested_typedef' checks that there is no type definition where two
  146. % levels of nested parentheses appear.
  147. %
  148. % ------------------------------------------------------------------------------
  149. %
  150.  
  151.  
  152. nested_typedef(TypeDefinition, type_definition) :->
  153. {
  154.   entry_test(parse_mode == prefix
  155.              && TypeDefinition == type_definition),
  156.   Type = TypeDefinition.type,
  157.   if(Type == protected_type && Type.type == protected_type) then
  158.     error_msg("Only one level of nested parentheses allowed around type definitions", 
  159.               TypeDefinition.name)
  160. }.
  161.  
  162.  
  163. %
  164. % ------------------------------------------------------------------------------
  165. %
  166. % This rule is the same that the previous one, except that the parentheses must
  167. % be contiguously nested.
  168. %
  169. % ------------------------------------------------------------------------------
  170. %
  171.  
  172. contiguous_nested_typedef(TypeDefinition, type_definition) :->
  173. {
  174.   entry_test(parse_mode == prefix
  175.              && TypeDefinition == type_definition),
  176.   Type = TypeDefinition.type,
  177.   if(Type == protected_type 
  178.      && Type.type == protected_type
  179.      && Type.left_parenthesis.next == "(") then
  180.     error_msg("Only one level of contiguous nested parentheses allowed around type definitions", 
  181.     TypeDefinition.name)
  182. }.
  183.  
  184.  
  185. %
  186. % ------------------------------------------------------------------------------
  187. %
  188. % The rule 'assignment_style' checks that all operators are preceded and
  189. % followed by a single space.
  190. %
  191. % ------------------------------------------------------------------------------
  192. %
  193.  
  194. assignment_style(Operator, operator) :->
  195. {
  196.   entry_test(parse_mode == prefix
  197.              && is_assignment_operator(Operator)),
  198.   if(Operator.white_spaces <> " "
  199.      || Operator.next.white_spaces <> " ") then
  200.     error_msg("Assignment operators must be preceded and followed by a single space",
  201.           Operator)
  202. }.
  203.  
  204.  
  205. %
  206. % ------------------------------------------------------------------------------
  207. %
  208. % The rule 'adjacent_operators' checks that two adjacent operators are
  209. % separated by blank spaces.
  210. %
  211. % ------------------------------------------------------------------------------
  212. %
  213.  
  214. adjacent_operators(Operator, operator) :->
  215. {
  216.   entry_test(parse_mode == prefix
  217.              && is_operator(Operator) && is_token(Operator)),
  218.   if(is_operator(Operator.previous) && Operator.white_spaces == "") then
  219.     error_msg("Two operators cannot be adjacent",
  220.           Operator)
  221. }.
  222.  
  223.  
  224. %
  225. % ------------------------------------------------------------------------------
  226. %
  227. %  Checking for uninitialized variables :
  228. %  --------------------------------------
  229. %
  230. %  When an identifier is found we first check if it is a local variable (for
  231. %  global variables we must consider the order of the function calls and the
  232. %  aliases, which leads to a much more complex analysis). Here a variable is
  233. %  considered initialized when it figures in the left of an assignment 
  234. %  expression in the current scope.
  235. %  We mark an initialized variable by adding a feature 'init' to its local
  236. %  declaration (if the 'initialization' feature is 'nothing').
  237. %  There are two special cases for 'if' and the iteration instructions :
  238. %
  239. %  - 'if' : two features are added (for the case of an if...then...else) :
  240. %    'then_init_variables' and 'else_init_variables' which contain as features
  241. %    the names of the variables initialized in the corresponding body.
  242. %    When the instruction is checked in postfix parse, theses fields have been
  243. %    set by calls to 'set_initialized_variable' and then their intersection is
  244. %    computed. The result, if nonempty, is a list of variables which are
  245. %    then declared initialized and marked in the scope of the 'if' instruction.
  246. %    I haven't yet implemented this for the switch statement, since I haven't
  247. %    got the time to do it...
  248. %
  249. %  - iteration instructions : there's only one feature 'init_variables' added
  250. %    which contains the names of the variables initialized in the body of the
  251. %    loop. They are not propagated, since we don't know if the loop will
  252. %    be always executed.
  253. %
  254. %
  255. % ------------------------------------------------------------------------------
  256. %
  257.  
  258.  
  259. uninitialized_variable(Expression, semantics) :->
  260. {
  261.   entry_test(is_expression(Expression)),
  262.   Mode = Expression.mode,
  263.   if(parse_mode == prefix) then
  264.     switch(Mode, {
  265.       case(special, {
  266.         if(Expression.operator == "?") then {
  267.           verify_init(Expression.condition),
  268.           verify_init(Expression.then),
  269.           verify_init(Expression.else)
  270.         } else { 
  271.           if(Expression == protected_expression) then
  272.             verify_init(Expression.expression)
  273.           else { 
  274.             if(Expression == function_call) then {
  275.               verify_init(Expression.call),
  276.               verify_init_args(Expression.arguments.first_argument)
  277.             } else {
  278.               if(Expression == array_reference) then {
  279.                 verify_init(Expression.array),
  280.                 verify_init(Expression.index)
  281.               }
  282.               else  % it's a cast
  283.                 verify_init(Expression.expression)
  284.             }
  285.           }
  286.         }
  287.       }),
  288.       case(infix, {
  289.         BaseExpression = base_expression(Expression.left_expression),
  290.         if(Expression.operator == "="
  291.            && BaseExpression == identifier) 
  292.         then
  293.           set_feature(uninit_marked, true, BaseExpression)
  294.           %%% The variable is being assigned, we must mark it, thus
  295.           %%% discarding all initialization checkings when it will
  296.           %%% be tested (don't forget that the procedure parse_tree
  297.           %%% recursively tests all the nodes, so when this rule will
  298.           %%% be completed the left expression of the assignment node
  299.           %%% (i.e. this variable) will be checked).
  300.         else
  301.           verify_init(Expression.left_expression),
  302.         verify_init(Expression.right_expression)
  303.       }),
  304.       case({prefix, postfix},
  305.         verify_init(Expression.expression)
  306.       )
  307.     }),
  308.   if(parse_mode == postfix
  309.      && Mode == infix
  310.      && Expression.operator == "=") then {
  311.     BaseExpression = base_expression(Expression.left_expression),
  312.     if(BaseExpression == identifier) then {
  313.       Declaration = declaration_of(BaseExpression.id),
  314.       if(Declaration == local_declaration) then
  315.         set_initialized_variable(BaseExpression.id, current_instruction.scope)
  316.         %%% We must mark this variable since it is initialized in the
  317.         %%% current scope.
  318.     }
  319.   }
  320. }.
  321.  
  322.  
  323. procedure verify_init(Identifier) :->
  324. {
  325.   if(Identifier == identifier) then {
  326.     if(has_feature(uninit_marked, Identifier)) then
  327.       donothing
  328.     else {
  329.       Mention = first_mention_of(Identifier.id),
  330.       %%% This call tests whether the variable has been initialized in
  331.       %%% the current scope or not.
  332.       if(Mention == nothing) then
  333.         error_msg("Variable may be uninitialized", Identifier)
  334.     }
  335.   }
  336. }.
  337.  
  338.  
  339. procedure verify_init_args(Argument) :->
  340. {
  341.   if(Argument <> nothing) then {
  342.     verify_init(Argument.expression),
  343.     verify_init_args(Argument.next)
  344.   }
  345. }.
  346.  
  347.  
  348. procedure set_initialized_variable(What, Instruction) :->
  349. {
  350.   switch(Instruction, {
  351.     case(then_body, {
  352.       Condition = Instruction.instruction,
  353.       if(has_feature(then_init_variables, Condition)) then
  354.         donothing
  355.       else
  356.         set_feature(then_init_variables, variables, Condition),
  357.       set_feature(What, init, Condition.then_init_variables)
  358.     }),
  359.     case(else_body, {
  360.       Condition = Instruction.instruction,
  361.       if(has_feature(else_init_variables, Condition)) then
  362.         donothing
  363.       else
  364.         set_feature(else_init_variables, variables, Condition),
  365.       set_feature(What, init, Condition.else_init_variables)
  366.     }),
  367.     case({for, while, do_while}, {
  368.       if(has_feature(init_variables, Instruction)) then
  369.         donothing
  370.       else
  371.         set_feature(init_variables, variables, Instruction),
  372.       set_feature(What, init, Instruction.init_variables)
  373.     }),
  374.     case(block, {
  375.       Declarations = Instruction.block_declarations.local_declarations,
  376.       if(has_feature(What, Declarations)) then
  377.         set_feature(init, true, Declarations.What)
  378.       else
  379.         set_initialized_variable(What, Instruction.scope)
  380.     }),
  381.     case({nothing, function_body},
  382.       donothing
  383.     ),
  384.     default(
  385.       set_initialized_variable(What, Instruction.scope)
  386.     )
  387.   })
  388. }.
  389.  
  390.  
  391. %
  392. % ------------------------------------------------------------------------------
  393. %
  394.  
  395.  
  396. propagate_init(If, semantics) :->
  397. {
  398.   entry_test(If == (if) && is_not_token(If) && parse_mode == postfix),
  399.   InitVariables = intersect_init_variables(If),
  400.   set_initialized_variables(InitVariables, If.scope)
  401. }.
  402.  
  403.  
  404. procedure set_initialized_variables(InitVariables, Instruction) :->
  405. {
  406.   if(InitVariables == []) then
  407.     donothing
  408.   else {
  409.     set_initialized_variable(InitVariables.first_element, Instruction),
  410.     set_initialized_variables(InitVariables.tail, Instruction)
  411.   }
  412. }.
  413.  
  414.  
  415. function intersect_init_variables(If) :->
  416. {
  417.   if(has_feature(then_init_variables, If)) then {
  418.     if(has_feature(else_init_variables, If)) then
  419.       return intersection(features_list(If.then_init_variables), 
  420.                           features_list(If.else_init_variables))
  421.     else
  422.       return features_list(If.then_init_variables)
  423.   } else {
  424.     if(has_feature(else_init_variables, If)) then
  425.       return features_list(If.else_init_variables)
  426.     else
  427.       return []
  428.   }
  429. }.
  430.  
  431.  
  432. function intersection(List1, List2) :->
  433. {
  434.   if(List1 == []) then
  435.     return []
  436.   else {
  437.     V = List1.first_element,
  438.     Tail = intersection(List1.tail, List2),
  439.     if(member(V, List2)) then
  440.       return newlist(V, Tail)
  441.     else
  442.       return Tail
  443.   }
  444. }.
  445.  
  446.  
  447. %
  448. % ------------------------------------------------------------------------------
  449. %
  450. %  Utilities functions for the verification of the uninitialized variables
  451. %
  452. % ------------------------------------------------------------------------------
  453. %
  454.  
  455.  
  456. function base_expression(Expression) :->
  457. %%%
  458. %%% Remove useless parentheses in an expression
  459. %%%
  460. {
  461.   if(Expression == protected_expression) then
  462.     return base_expression(Expression.expression)
  463.   else
  464.     return Expression
  465. }.
  466.  
  467.  
  468. function is_expression(Node) :->
  469. {
  470.   if(has_feature(mode, Node)) then
  471.     return true
  472.   else
  473.     return false
  474. }.
  475.  
  476.  
  477. function first_mention_of(What) :->
  478. %%%
  479. %%% Returns nothing if 'What' is the name of a local variable which
  480. %%% has not been initialized within the current scope.
  481. %%%
  482. {
  483.   if(current_block == nothing) then
  484.     return external
  485.   else
  486.     return search_mention_of(What, current_block)
  487. }.
  488.  
  489.  
  490. function search_mention_of(What, Instruction) :->
  491. {
  492.   switch(Instruction, {
  493.     case(block, {
  494.       Declarations = Instruction.block_declarations.local_declarations,
  495.       if(has_feature(What, Declarations)) then {
  496.         if(Declarations.What.initialization <> nothing
  497.            || has_feature(init, Declarations.What)) then
  498.           return init
  499.         else
  500.           return nothing
  501.       } else
  502.         return search_mention_of(What, Instruction.scope)
  503.     }),
  504.     case(then_body, {
  505.       Condition = Instruction.instruction,
  506.       if(has_feature(then_init_variables, Condition)
  507.          && has_feature(What, Condition.then_init_variables)) then
  508.         return init
  509.       else
  510.         return search_mention_of(What, Condition.scope)
  511.     }),
  512.     case(else_body, {
  513.       Condition = Instruction.instruction,
  514.       if(has_feature(else_init_variables, Condition)
  515.          && has_feature(What, Condition.else_init_variables)) then
  516.         return init
  517.       else
  518.         return search_mention_of(What, Condition.scope)
  519.     }),
  520.     case({for, while, do_while}, {
  521.       if(has_feature(init_variables, Instruction)
  522.          && has_feature(What, Instruction.init_variables)) then
  523.         return init
  524.       else
  525.         return search_mention_of(What, Instruction.scope)
  526.     }),
  527.     case({nothing, function_body},
  528.       return external
  529.     ),
  530.     default(
  531.       search_mention_of(What, Instruction.scope)
  532.     )
  533.   })
  534. }.
  535.  
  536.  
  537. function declaration_of(What) :->
  538. %%%
  539. %%% Returns the 'local_declaration' structure corresponding to 'What'
  540. %%% if it's the name of a local variable, nothing otherwise.
  541. %%%
  542. {
  543.   if(current_block == nothing) then
  544.     return nothing
  545.   else
  546.     return search_declaration(current_block.block_declarations, What)
  547. }.
  548.  
  549.  
  550. function search_declaration(Scope, What) :->
  551. {
  552.   if(Scope == block_declarations) then
  553.     Declarations = Scope.local_declarations
  554.   else
  555.     Declarations = nothing,
  556.   if(Declarations == nothing) then
  557.     return nothing
  558.   else {
  559.     if(has_feature(What, Declarations)) then
  560.       return Declarations.What
  561.     else
  562.       return search_declaration(Scope.parent_declarations, What)
  563.   }
  564. }.
  565.  
  566.  
  567. %
  568. % ------------------------------------------------------------------------------
  569. %
  570. % The main procedure
  571. %
  572. % ------------------------------------------------------------------------------
  573. %
  574.  
  575. main :->
  576.  {
  577.   writeln,
  578.   writeln("Style checking : "),
  579.   writeln("----------------"),
  580.   writeln,
  581.   scan_tree({operator, type_definition, alignment, 
  582.              declaration, variable_length}),
  583.   writeln,
  584.   writeln("Uninitialized variables : "),
  585.   writeln("-------------------------"),  
  586.   writeln,
  587.   scan_tree(semantics)
  588.  }.
  589.  
  590.  
  591. %
  592. % ------------------------------------------------------------------------------
  593. %
  594.  
  595.  
  596.  
  597.  
  598.  
  599.  
  600.