adlint 1.16.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. data/ChangeLog +471 -0
  2. data/MANIFEST +35 -8
  3. data/NEWS +50 -4
  4. data/bin/adlint +7 -7
  5. data/bin/adlint_chk +7 -7
  6. data/bin/adlint_cma +7 -7
  7. data/bin/adlint_sma +7 -7
  8. data/bin/adlintize +5 -5
  9. data/etc/mesg.d/en_US/messages.yml +3 -3
  10. data/etc/mesg.d/ja_JP/messages.yml +3 -3
  11. data/features/message_detection/E0013.feature +34 -0
  12. data/features/message_detection/W0007.feature +2 -0
  13. data/features/message_detection/W0583.feature +1 -2
  14. data/features/message_detection/W0641.feature +132 -0
  15. data/features/message_detection/W0643.feature +1 -1
  16. data/features/message_detection/W0644.feature +529 -0
  17. data/features/message_detection/W0645.feature +1 -1
  18. data/features/message_detection/W0649.feature +277 -0
  19. data/features/message_detection/W0650.feature +208 -0
  20. data/features/message_detection/W0697.feature +6 -0
  21. data/features/message_detection/W0705.feature +350 -0
  22. data/features/message_detection/W0707.feature +223 -0
  23. data/features/message_detection/W0711.feature +113 -0
  24. data/features/message_detection/W0712.feature +113 -0
  25. data/features/message_detection/W0713.feature +110 -0
  26. data/features/message_detection/W0714.feature +118 -0
  27. data/features/message_detection/W0715.feature +118 -0
  28. data/features/message_detection/W0716.feature +1 -0
  29. data/features/message_detection/W0717.feature +1 -0
  30. data/features/message_detection/W0718.feature +1 -0
  31. data/features/message_detection/W0719.feature +154 -0
  32. data/features/message_detection/W0723.feature +1 -2
  33. data/features/message_detection/W0732.feature +3 -0
  34. data/features/message_detection/W0733.feature +3 -0
  35. data/features/message_detection/W0734.feature +4 -0
  36. data/features/message_detection/W0735.feature +4 -0
  37. data/features/message_detection/W0745.feature +132 -0
  38. data/features/message_detection/W0780.feature +68 -0
  39. data/features/message_detection/W0783.feature +95 -0
  40. data/features/message_detection/W0792.feature +124 -0
  41. data/features/message_detection/W0793.feature +153 -0
  42. data/features/message_detection/W0794.feature +90 -0
  43. data/features/message_detection/W0830.feature +65 -0
  44. data/features/message_detection/W0833.feature +220 -0
  45. data/features/message_detection/W0834.feature +189 -0
  46. data/features/message_detection/W1026.feature +105 -0
  47. data/features/message_detection/W1031.feature +17 -34
  48. data/features/message_detection/W1039.feature +268 -0
  49. data/features/message_detection/W1047.feature +163 -0
  50. data/features/message_detection/W1066.feature +1 -0
  51. data/features/message_detection/W1067.feature +1 -0
  52. data/features/message_detection/W1068.feature +1 -0
  53. data/features/message_detection/W1069.feature +5 -0
  54. data/features/message_detection/W1070.feature +5 -0
  55. data/features/message_detection/W1071.feature +83 -0
  56. data/features/message_detection/W1073.feature +3 -2
  57. data/features/message_detection/W9003.feature +7 -12
  58. data/features/step_definitions/message_detection_steps.rb +4 -31
  59. data/features/support/env.rb +117 -2
  60. data/lib/adlint/c/branch.rb +0 -2
  61. data/lib/adlint/c/ctrlexpr.rb +33 -0
  62. data/lib/adlint/c/expr.rb +119 -31
  63. data/lib/adlint/c/interp.rb +44 -3
  64. data/lib/adlint/c/message.rb +1411 -29
  65. data/lib/adlint/c/object.rb +16 -2
  66. data/lib/adlint/c/option.rb +1 -0
  67. data/lib/adlint/c/parser.rb +101 -100
  68. data/lib/adlint/c/parser.y +3 -2
  69. data/lib/adlint/c/phase.rb +18 -0
  70. data/lib/adlint/c/resolver.rb +23 -0
  71. data/lib/adlint/c/syntax.rb +90 -4
  72. data/lib/adlint/c/type.rb +177 -110
  73. data/lib/adlint/cpp/macro.rb +4 -4
  74. data/lib/adlint/version.rb +2 -2
  75. data/share/demo/bad_include/test/"1/".h +0 -0
  76. data/share/doc/developers_guide_ja.html +3 -3
  77. data/share/doc/developers_guide_ja.texi +1 -1
  78. data/share/doc/users_guide_en.html +467 -506
  79. data/share/doc/users_guide_en.texi +95 -125
  80. data/share/doc/users_guide_ja.html +471 -518
  81. data/share/doc/users_guide_ja.texi +95 -133
  82. data/spec/spec_helper.rb +6 -0
  83. metadata +37 -10
@@ -71,6 +71,7 @@ module C #:nodoc:
71
71
  include InterpreterMediator
72
72
  include Conversion
73
73
  include InterpreterOptions
74
+ include BranchOptions
74
75
 
75
76
  def initialize(type_table)
76
77
  type_table = type_table
@@ -426,7 +427,17 @@ module C #:nodoc:
426
427
 
427
428
  def execute(node, *options)
428
429
  @options_stack.push(current_options + options)
429
- node.accept(interpreter_for(node))
430
+ if quiet_without_side_effect?
431
+ result = nil
432
+ branched_eval(nil, FINAL) do
433
+ result = node.accept(interpreter_for(node))
434
+ # NOTE: To rollback latest variable value versions.
435
+ BreakEvent.of_return.throw
436
+ end
437
+ else
438
+ result = node.accept(interpreter_for(node))
439
+ end
440
+ result
430
441
  ensure
431
442
  @options_stack.pop
432
443
  end
@@ -456,7 +467,8 @@ module C #:nodoc:
456
467
  end
457
468
 
458
469
  def quiet?
459
- current_options.include?(QUIET)
470
+ current_options.include?(QUIET) ||
471
+ current_options.include?(QUIET_WITHOUT_SIDE_EFFECT)
460
472
  end
461
473
 
462
474
  def _quiet=(quiet)
@@ -468,6 +480,10 @@ module C #:nodoc:
468
480
  end
469
481
  end
470
482
 
483
+ def quiet_without_side_effect?
484
+ current_options.include?(QUIET_WITHOUT_SIDE_EFFECT)
485
+ end
486
+
471
487
  def _active_function
472
488
  # NOTE: This method is called only from
473
489
  # StatementInterpreter#visit_return_statement.
@@ -726,11 +742,36 @@ module C #:nodoc:
726
742
  end
727
743
 
728
744
  def evaluate_initializers(initializers, variable_type)
745
+ # NOTE: The ISO C99 standard saids;
746
+ #
747
+ # 6.7.8 Initialization
748
+ #
749
+ # Semantics
750
+ #
751
+ # 10 If an object that has automatic storage duration is not initialized
752
+ # explicitly, its value is indeterminate. If an object that has
753
+ # static storage duration is not initialized explicitly, then:
754
+ # -- if it has pointer type, it is initialized to a null pointer;
755
+ # -- if it has arithmetic type, it is initialized to (positive or
756
+ # unsigned) zero;
757
+ # -- if it is an aggregate, every member is initialized (recursively)
758
+ # according to these rules;
759
+ # -- if it is a union, the first named member is initialized
760
+ # (recursively) according to these rules.
761
+ if variable_type.union?
762
+ if first_member = variable_type.members.first
763
+ first_object = evaluate_initializers(initializers, first_member.type)
764
+ return temporary_variable(variable_type, value_of(first_object))
765
+ else
766
+ return temporary_variable(variable_type)
767
+ end
768
+ end
769
+
729
770
  case
730
771
  when variable_type.array?
731
772
  deduct_array_length_from_initializers(variable_type, initializers)
732
773
  member_types = [variable_type.base_type] * variable_type.impl_length
733
- when variable_type.composite?
774
+ when variable_type.struct?
734
775
  member_types = variable_type.members.map { |member| member.type }
735
776
  else
736
777
  member_types = [variable_type]
@@ -31,9 +31,11 @@
31
31
 
32
32
  require "adlint/report"
33
33
  require "adlint/message"
34
+ require "adlint/token"
34
35
  require "adlint/traits"
35
36
  require "adlint/monitor"
36
37
  require "adlint/c/syntax"
38
+ require "adlint/c/type"
37
39
  require "adlint/c/format"
38
40
  require "adlint/c/option"
39
41
  require "adlint/c/util"
@@ -1832,7 +1834,7 @@ module C #:nodoc:
1832
1834
  end
1833
1835
 
1834
1836
  def write_variable(expression, variable)
1835
- variable = variable.owner while variable.kind_of?(InnerVariable)
1837
+ variable = variable.owner while variable.inner?
1836
1838
  return unless variable.named?
1837
1839
 
1838
1840
  @variable_stack.reverse_each do |variables|
@@ -6933,6 +6935,7 @@ module C #:nodoc:
6933
6935
  return if variable.scope.global? || variable.binding.memory.static?
6934
6936
 
6935
6937
  if variable.named? && variable.value.must_be_undefined?
6938
+ variable = variable.owner while variable.inner?
6936
6939
  W(:W0459, expression.location, variable.name)
6937
6940
  end
6938
6941
  end
@@ -11539,6 +11542,29 @@ module C #:nodoc:
11539
11542
  end
11540
11543
  end
11541
11544
 
11545
+ class W0641 < PassiveMessageDetection
11546
+ def initialize(context)
11547
+ super
11548
+ interp = context[:c_interpreter]
11549
+ interp.on_explicit_conv_performed += method(:check)
11550
+ end
11551
+
11552
+ private
11553
+ def check(expression, original_variable, result_variable)
11554
+ lhs_type = original_variable.type.unqualify
11555
+ rhs_type = result_variable.type.unqualify
11556
+
11557
+ case
11558
+ when lhs_type.floating? &&
11559
+ rhs_type.pointer? && !rhs_type.base_type.function?
11560
+ W(:W0641, expression.location)
11561
+ when rhs_type.floating? &&
11562
+ lhs_type.pointer? && !lhs_type.base_type.function?
11563
+ W(:W0641, expression.location)
11564
+ end
11565
+ end
11566
+ end
11567
+
11542
11568
  class W0642 < PassiveMessageDetection
11543
11569
  def initialize(context)
11544
11570
  super
@@ -11561,6 +11587,27 @@ module C #:nodoc:
11561
11587
  end
11562
11588
  end
11563
11589
 
11590
+ class W0644 < PassiveMessageDetection
11591
+ # NOTE: W0644 may be duplicative when both hand side of a binary-expression
11592
+ # refer a value of `void' expression.
11593
+ ensure_uniqueness_of :W0644
11594
+
11595
+ def initialize(context)
11596
+ super
11597
+ interp = context[:c_interpreter]
11598
+
11599
+ interp.on_variable_value_referred += method(:check)
11600
+ interp.on_explicit_conv_performed += lambda { |expr, from, to|
11601
+ check(expr, from)
11602
+ }
11603
+ end
11604
+
11605
+ private
11606
+ def check(expression, variable)
11607
+ W(:W0644, expression.location) if variable.type.void?
11608
+ end
11609
+ end
11610
+
11564
11611
  class W0646 < PassiveMessageDetection
11565
11612
  def initialize(context)
11566
11613
  super
@@ -11577,6 +11624,45 @@ module C #:nodoc:
11577
11624
  end
11578
11625
  end
11579
11626
 
11627
+ class W0649 < PassiveMessageDetection
11628
+ def initialize(context)
11629
+ super
11630
+ interp = context[:c_interpreter]
11631
+ interp.on_shift_expr_evaled += method(:check)
11632
+ @enum_tbl = interp.environment.enumerator_table
11633
+ end
11634
+
11635
+ private
11636
+ def check(expr, lhs_variable, rhs_variable, result_variable)
11637
+ if expr.rhs_operand.constant?(@enum_tbl)
11638
+ if rhs_variable.value.must_be_less_than?(ScalarValue.of(0))
11639
+ W(:W0649, expr.location)
11640
+ end
11641
+ end
11642
+ end
11643
+ end
11644
+
11645
+ class W0650 < PassiveMessageDetection
11646
+ def initialize(context)
11647
+ super
11648
+ interp = context[:c_interpreter]
11649
+ interp.on_shift_expr_evaled += method(:check)
11650
+ @enum_tbl = interp.environment.enumerator_table
11651
+ end
11652
+
11653
+ private
11654
+ def check(expr, lhs_variable, rhs_variable, result_variable)
11655
+ if expr.rhs_operand.constant?(@enum_tbl)
11656
+ promoted_type = lhs_variable.type.integer_promoted_type
11657
+ promoted_bit_size = ScalarValue.of(promoted_type.bit_size)
11658
+ if rhs_variable.value.must_be_equal_to?(promoted_bit_size) ||
11659
+ rhs_variable.value.must_be_greater_than?(promoted_bit_size)
11660
+ W(:W0650, expr.location)
11661
+ end
11662
+ end
11663
+ end
11664
+ end
11665
+
11580
11666
  class W0653 < PassiveMessageDetection
11581
11667
  def initialize(context)
11582
11668
  super
@@ -12314,30 +12400,624 @@ module C #:nodoc:
12314
12400
  end
12315
12401
 
12316
12402
  class W0705 < PassiveMessageDetection
12403
+ include SyntaxNodeCollector
12404
+ include InterpreterOptions
12405
+
12317
12406
  def initialize(context)
12318
12407
  super
12319
- interp = context[:c_interpreter]
12320
- interp.on_array_subscript_expr_evaled += method(:check)
12408
+ @interp = context[:c_interpreter]
12409
+ @interp.on_array_subscript_expr_evaled += method(:check_array_subscript)
12410
+ @interp.on_indirection_expr_evaled += method(:check_indirection)
12411
+ @enum_tbl = @interp.environment.enumerator_table
12321
12412
  end
12322
12413
 
12323
12414
  private
12324
- def check(array_subscript_expression,
12325
- array_or_pointer_variable, subscript_variable,
12326
- array_variable, result_variable)
12327
- return unless array_variable && array_variable.type.length
12328
- return unless subscript_variable.value.scalar?
12415
+ def check_array_subscript(expression, *, subscript, array, result)
12416
+ return unless array
12329
12417
 
12330
- lower_bound = ScalarValue.of(0)
12331
- upper_bound = ScalarValue.of(array_variable.type.length - 1)
12418
+ unless expression.array_subscript.constant?(@enum_tbl)
12419
+ warn_array_oob_access(expression.array_subscript, array, subscript)
12420
+ end
12421
+ end
12332
12422
 
12333
- lower_test = subscript_variable.value < lower_bound
12334
- upper_test = subscript_variable.value > upper_bound
12423
+ def check_indirection(expression, *)
12424
+ array, subscript_expr = extract_array_and_subscript(expression.operand)
12425
+ return unless array
12335
12426
 
12336
- if !lower_test.must_be_true? && lower_test.may_be_true? or
12337
- !upper_test.must_be_true? && upper_test.may_be_true?
12338
- W(:W0705, array_subscript_expression.array_subscript.location)
12427
+ unless subscript_expr.constant?(@enum_tbl)
12428
+ subscript = @interp.interpret(subscript_expr,
12429
+ QUIET_WITHOUT_SIDE_EFFECT)
12430
+ warn_array_oob_access(expression.operand, array, subscript)
12431
+ end
12432
+ end
12433
+
12434
+ def warn_array_oob_access(expression, array, subscript)
12435
+ if array_length = array.type.length
12436
+ lower_bound = ScalarValue.of(0)
12437
+ upper_bound = ScalarValue.of(array_length - 1)
12438
+
12439
+ lower_test = subscript.value < lower_bound
12440
+ upper_test = subscript.value > upper_bound
12441
+
12442
+ if !lower_test.must_be_true? && lower_test.may_be_true? or
12443
+ !upper_test.must_be_true? && upper_test.may_be_true?
12444
+ W(:W0705, expression.location)
12445
+ end
12446
+ end
12447
+ end
12448
+
12449
+ def extract_array_and_subscript(expression)
12450
+ array, array_name = extract_array(expression)
12451
+ subscript_expr = create_subscript_expr(expression, array_name)
12452
+ return array, subscript_expr
12453
+ end
12454
+
12455
+ def extract_array(expression)
12456
+ collect_object_specifiers(expression).each do |os|
12457
+ if object = @interp.variable_named(os.identifier.value)
12458
+ case
12459
+ when object.type.array?
12460
+ return object, os.identifier.value
12461
+ when object.type.pointer?
12462
+ if object = @interp.pointee_of(object) and object.type.array?
12463
+ return object, os.identifier.value
12464
+ end
12465
+ end
12466
+ end
12467
+ end
12468
+ nil
12469
+ end
12470
+
12471
+ def create_subscript_expr(expression, array_name)
12472
+ expression.accept(ExpressionTransformer.new(array_name))
12473
+ end
12474
+
12475
+ class ExpressionTransformer < SyntaxTreeVisitor
12476
+ def initialize(array_name)
12477
+ @array_name = array_name
12478
+ end
12479
+
12480
+ def visit_error_expression(node)
12481
+ node
12482
+ end
12483
+
12484
+ def visit_object_specifier(node)
12485
+ if node.identifier.value == @array_name
12486
+ ConstantSpecifier.new(Token.new(:CONSTANT, "0", node.location))
12487
+ else
12488
+ node
12489
+ end
12490
+ end
12491
+
12492
+ def visit_constant_specifier(node)
12493
+ node
12494
+ end
12495
+
12496
+ def visit_string_literal_specifier(node)
12497
+ node
12498
+ end
12499
+
12500
+ def visit_null_constant_specifier(node)
12501
+ node
12502
+ end
12503
+
12504
+ def visit_grouped_expression(node)
12505
+ GroupedExpression.new(node.expression.accept(self))
12506
+ end
12507
+
12508
+ def visit_array_subscript_expression(node)
12509
+ ArraySubscriptExpression.new(node.expression.accept(self),
12510
+ node.array_subscript.accept(self),
12511
+ node.operator)
12512
+ end
12513
+
12514
+ def visit_function_call_expression(node)
12515
+ FunctionCallExpression.new(
12516
+ node.expression.accept(self),
12517
+ node.argument_expressions.map { |expr| expr.accept(self) },
12518
+ node.operator)
12519
+ end
12520
+
12521
+ def visit_member_access_by_value_expression(node)
12522
+ MemberAccessByValueExpression.new(node.expression.accept(self),
12523
+ node.identifier, node.operator)
12524
+ end
12525
+
12526
+ def visit_member_access_by_pointer_expression(node)
12527
+ MemberAccessByPointerExpression.new(node.expression.accept(self),
12528
+ node.identifier, node.operator)
12529
+ end
12530
+
12531
+ def visit_bit_access_by_value_expression(node)
12532
+ BitAccessByValueExpression.new(node.expression.accept(self),
12533
+ node.constant, node.operator)
12534
+ end
12535
+
12536
+ def visit_bit_access_by_pointer_expression(node)
12537
+ BitAccessByPointerExpression.new(node.expression.accept(self),
12538
+ node.constant, node.operator)
12539
+ end
12540
+
12541
+ def visit_postfix_increment_expression(node)
12542
+ # NOTE: The postfix-increment-expression is already evaluated with
12543
+ # side-effect.
12544
+ # To rollback the side-effect, create an inverted expression.
12545
+ PrefixDecrementExpression.new(
12546
+ Token.new("--", "--", node.operator.location),
12547
+ node.operand.accept(self))
12548
+ end
12549
+
12550
+ def visit_postfix_decrement_expression(node)
12551
+ # NOTE: The postfix-decrement-expression is already evaluated with
12552
+ # side-effect.
12553
+ # To rollback the side-effect, create an inverted expression.
12554
+ PrefixIncrementExpression.new(
12555
+ Token.new("++", "++", node.operator.location),
12556
+ node.operand.accept(self))
12557
+ end
12558
+
12559
+ def visit_compound_literal_expression(node)
12560
+ CompoundLiteralExpression.new(
12561
+ node.type_name, node.initializers.map { |ini| ini.accept(self) },
12562
+ node.operator)
12563
+ end
12564
+
12565
+ def visit_prefix_increment_expression(node)
12566
+ # NOTE: The prefix-increment-expression is already evaluated with
12567
+ # side-effect.
12568
+ # To rollback the side-effect, create an inverted expression.
12569
+ PostfixDecrementExpression.new(
12570
+ Token.new("--", "--", node.operator.location),
12571
+ node.operand.accept(self))
12572
+ end
12573
+
12574
+ def visit_prefix_decrement_expression(node)
12575
+ # NOTE: The prefix-decrement-expression is already evaluated with
12576
+ # side-effect.
12577
+ # To rollback the side-effect, create an inverted expression.
12578
+ PostfixIncrementExpression.new(
12579
+ Token.new("++", "++", node.operator.location),
12580
+ node.operand.accept(self))
12581
+ end
12582
+
12583
+ def visit_address_expression(node)
12584
+ AddressExpression.new(node.operator, node.operand.accept(self))
12585
+ end
12586
+
12587
+ def visit_indirection_expression(node)
12588
+ IndirectionExpression.new(node.operator, node.operand.accept(self))
12589
+ end
12590
+
12591
+ def visit_unary_arithmetic_expression(node)
12592
+ UnaryArithmeticExpression.new(node.operator, node.operand.accept(self))
12593
+ end
12594
+
12595
+ def visit_sizeof_expression(node)
12596
+ SizeofExpression.new(node.operator, node.operand.accept(self))
12597
+ end
12598
+
12599
+ def visit_sizeof_type_expression(node)
12600
+ node
12601
+ end
12602
+
12603
+ def visit_alignof_expression(node)
12604
+ AlignofExpression.new(node.operator, node.operand.accept(self))
12605
+ end
12606
+
12607
+ def visit_alignof_type_expression(node)
12608
+ node
12609
+ end
12610
+
12611
+ def visit_cast_expression(node)
12612
+ CastExpression.new(node.type_name, node.operand.accept(self))
12613
+ end
12614
+
12615
+ def visit_multiplicative_expression(node)
12616
+ MultiplicativeExpression.new(node.operator,
12617
+ node.lhs_operand.accept(self),
12618
+ node.rhs_operand.accept(self))
12619
+ end
12620
+
12621
+ def visit_additive_expression(node)
12622
+ AdditiveExpression.new(node.operator,
12623
+ node.lhs_operand.accept(self),
12624
+ node.rhs_operand.accept(self))
12625
+ end
12626
+
12627
+ def visit_shift_expression(node)
12628
+ ShiftExpression.new(node.operator,
12629
+ node.lhs_operand.accept(self),
12630
+ node.rhs_operand.accept(self))
12631
+ end
12632
+
12633
+ def visit_relational_expression(node)
12634
+ RelationalExpression.new(node.operator,
12635
+ node.lhs_operand.accept(self),
12636
+ node.rhs_operand.accept(self))
12637
+ end
12638
+
12639
+ def visit_equality_expression(node)
12640
+ EqualityExpression.new(node.operator,
12641
+ node.lhs_operand.accept(self),
12642
+ node.rhs_operand.accept(self))
12643
+ end
12644
+
12645
+ def visit_and_expression(node)
12646
+ AndExpression.new(node.operator,
12647
+ node.lhs_operand.accept(self),
12648
+ node.rhs_operand.accept(self))
12649
+ end
12650
+
12651
+ def visit_exclusive_or_expression(node)
12652
+ ExclusiveOrExpression.new(node.operator,
12653
+ node.lhs_operand.accept(self),
12654
+ node.rhs_operand.accept(self))
12655
+ end
12656
+
12657
+ def visit_inclusive_or_expression(node)
12658
+ InclusiveOrExpression.new(node.operator,
12659
+ node.lhs_operand.accept(self),
12660
+ node.rhs_operand.accept(self))
12661
+ end
12662
+
12663
+ def visit_logical_and_expression(node)
12664
+ LogicalAndExpression.new(node.operator,
12665
+ node.lhs_operand.accept(self),
12666
+ node.rhs_operand.accept(self))
12667
+ end
12668
+
12669
+ def visit_logical_or_expression(node)
12670
+ LogicalOrExpression.new(node.operator,
12671
+ node.lhs_operand.accept(self),
12672
+ node.rhs_operand.accept(self))
12673
+ end
12674
+
12675
+ def visit_conditional_expression(node)
12676
+ ConditionalExpression.new(node.condition.accept(self),
12677
+ node.then_expression.accept(self),
12678
+ node.else_expression.accept(self),
12679
+ Token.new("?", "?", node.location))
12680
+ end
12681
+
12682
+ def visit_simple_assignment_expression(node)
12683
+ SimpleAssignmentExpression.new(node.operator,
12684
+ node.lhs_operand.accept(self),
12685
+ node.rhs_operand.accept(self))
12686
+ end
12687
+
12688
+ def visit_compound_assignment_expression(node)
12689
+ CompoundAssignmentExpression.new(node.operator,
12690
+ node.lhs_operand.accept(self),
12691
+ node.rhs_operand.accept(self))
12692
+ end
12693
+
12694
+ def visit_comma_separated_expression(node)
12695
+ expressions = node.expressions.map { |expr| expr.accept(self) }
12696
+ result = CommaSeparatedExpression.new(expressions.shift)
12697
+ expressions.each { |expr| result.expressions.push(expr) }
12698
+ result
12699
+ end
12700
+
12701
+ def visit_initializer(node)
12702
+ case
12703
+ when node.expression
12704
+ Initializer.new(node.expression.accept(self), nil)
12705
+ when node.initializers
12706
+ Initializer.new(nil, node.initializers.map { |i| i.accept(self) })
12707
+ end
12339
12708
  end
12340
12709
  end
12710
+ private_constant :ExpressionTransformer
12711
+ end
12712
+
12713
+ class W0707 < PassiveMessageDetection
12714
+ include SyntaxNodeCollector
12715
+ include InterpreterOptions
12716
+
12717
+ def initialize(context)
12718
+ super
12719
+ @interp = context[:c_interpreter]
12720
+ @interp.on_array_subscript_expr_evaled += method(:check_array_subscript)
12721
+ @interp.on_indirection_expr_evaled += method(:check_indirection)
12722
+ @enum_tbl = @interp.environment.enumerator_table
12723
+ end
12724
+
12725
+ private
12726
+ def check_array_subscript(expression, *, subscript, array, result)
12727
+ return unless array
12728
+
12729
+ if expression.array_subscript.constant?(@enum_tbl)
12730
+ warn_array_oob_access(expression.array_subscript, array, subscript)
12731
+ end
12732
+ end
12733
+
12734
+ def check_indirection(expression, *)
12735
+ array, subscript_expr = extract_array_and_subscript(expression.operand)
12736
+ return unless array
12737
+
12738
+ if subscript_expr.constant?(@enum_tbl)
12739
+ # NOTE: A constant-expression has no side-effects.
12740
+ subscript = @interp.interpret(subscript_expr, QUIET)
12741
+ warn_array_oob_access(expression.operand, array, subscript)
12742
+ end
12743
+ end
12744
+
12745
+ def warn_array_oob_access(expression, array, subscript)
12746
+ if array_length = array.type.length
12747
+ lower_bound = ScalarValue.of(0)
12748
+ upper_bound = ScalarValue.of(array_length - 1)
12749
+
12750
+ lower_test = subscript.value < lower_bound
12751
+ upper_test = subscript.value > upper_bound
12752
+
12753
+ if lower_test.must_be_true? || upper_test.must_be_true?
12754
+ W(:W0707, expression.location)
12755
+ end
12756
+ end
12757
+ end
12758
+
12759
+ def extract_array_and_subscript(expression)
12760
+ array, array_name = extract_array(expression)
12761
+ subscript_expr = create_subscript_expr(expression, array_name)
12762
+ return array, subscript_expr
12763
+ end
12764
+
12765
+ def extract_array(expression)
12766
+ collect_object_specifiers(expression).each do |os|
12767
+ if object = @interp.variable_named(os.identifier.value)
12768
+ case
12769
+ when object.type.array?
12770
+ return object, os.identifier.value
12771
+ when object.type.pointer?
12772
+ if object = @interp.pointee_of(object) and object.type.array?
12773
+ return object, os.identifier.value
12774
+ end
12775
+ end
12776
+ end
12777
+ end
12778
+ nil
12779
+ end
12780
+
12781
+ def create_subscript_expr(expression, array_name)
12782
+ expression.accept(ExpressionTransformer.new(array_name))
12783
+ end
12784
+
12785
+ class ExpressionTransformer < SyntaxTreeVisitor
12786
+ def initialize(array_name)
12787
+ @array_name = array_name
12788
+ end
12789
+
12790
+ def visit_error_expression(node)
12791
+ node
12792
+ end
12793
+
12794
+ def visit_object_specifier(node)
12795
+ if node.identifier.value == @array_name
12796
+ ConstantSpecifier.new(Token.new(:CONSTANT, "0", node.location))
12797
+ else
12798
+ node
12799
+ end
12800
+ end
12801
+
12802
+ def visit_constant_specifier(node)
12803
+ node
12804
+ end
12805
+
12806
+ def visit_string_literal_specifier(node)
12807
+ node
12808
+ end
12809
+
12810
+ def visit_null_constant_specifier(node)
12811
+ node
12812
+ end
12813
+
12814
+ def visit_grouped_expression(node)
12815
+ GroupedExpression.new(node.expression.accept(self))
12816
+ end
12817
+
12818
+ def visit_array_subscript_expression(node)
12819
+ ArraySubscriptExpression.new(node.expression.accept(self),
12820
+ node.array_subscript.accept(self),
12821
+ node.operator)
12822
+ end
12823
+
12824
+ def visit_function_call_expression(node)
12825
+ FunctionCallExpression.new(
12826
+ node.expression.accept(self),
12827
+ node.argument_expressions.map { |expr| expr.accept(self) },
12828
+ node.operator)
12829
+ end
12830
+
12831
+ def visit_member_access_by_value_expression(node)
12832
+ MemberAccessByValueExpression.new(node.expression.accept(self),
12833
+ node.identifier, node.operator)
12834
+ end
12835
+
12836
+ def visit_member_access_by_pointer_expression(node)
12837
+ MemberAccessByPointerExpression.new(node.expression.accept(self),
12838
+ node.identifier, node.operator)
12839
+ end
12840
+
12841
+ def visit_bit_access_by_value_expression(node)
12842
+ BitAccessByValueExpression.new(node.expression.accept(self),
12843
+ node.constant, node.operator)
12844
+ end
12845
+
12846
+ def visit_bit_access_by_pointer_expression(node)
12847
+ BitAccessByPointerExpression.new(node.expression.accept(self),
12848
+ node.constant, node.operator)
12849
+ end
12850
+
12851
+ def visit_postfix_increment_expression(node)
12852
+ # NOTE: The postfix-increment-expression is already evaluated with
12853
+ # side-effect.
12854
+ # To rollback the side-effect, create an inverted expression.
12855
+ PrefixDecrementExpression.new(
12856
+ Token.new("--", "--", node.operator.location),
12857
+ node.operand.accept(self))
12858
+ end
12859
+
12860
+ def visit_postfix_decrement_expression(node)
12861
+ # NOTE: The postfix-decrement-expression is already evaluated with
12862
+ # side-effect.
12863
+ # To rollback the side-effect, create an inverted expression.
12864
+ PrefixIncrementExpression.new(
12865
+ Token.new("++", "++", node.operator.location),
12866
+ node.operand.accept(self))
12867
+ end
12868
+
12869
+ def visit_compound_literal_expression(node)
12870
+ CompoundLiteralExpression.new(
12871
+ node.type_name, node.initializers.map { |ini| ini.accept(self) },
12872
+ node.operator)
12873
+ end
12874
+
12875
+ def visit_prefix_increment_expression(node)
12876
+ # NOTE: The prefix-increment-expression is already evaluated with
12877
+ # side-effect.
12878
+ # To rollback the side-effect, create an inverted expression.
12879
+ PostfixDecrementExpression.new(
12880
+ Token.new("--", "--", node.operator.location),
12881
+ node.operand.accept(self))
12882
+ end
12883
+
12884
+ def visit_prefix_decrement_expression(node)
12885
+ # NOTE: The prefix-decrement-expression is already evaluated with
12886
+ # side-effect.
12887
+ # To rollback the side-effect, create an inverted expression.
12888
+ PostfixIncrementExpression.new(
12889
+ Token.new("++", "++", node.operator.location),
12890
+ node.operand.accept(self))
12891
+ end
12892
+
12893
+ def visit_address_expression(node)
12894
+ AddressExpression.new(node.operator, node.operand.accept(self))
12895
+ end
12896
+
12897
+ def visit_indirection_expression(node)
12898
+ IndirectionExpression.new(node.operator, node.operand.accept(self))
12899
+ end
12900
+
12901
+ def visit_unary_arithmetic_expression(node)
12902
+ UnaryArithmeticExpression.new(node.operator, node.operand.accept(self))
12903
+ end
12904
+
12905
+ def visit_sizeof_expression(node)
12906
+ SizeofExpression.new(node.operator, node.operand.accept(self))
12907
+ end
12908
+
12909
+ def visit_sizeof_type_expression(node)
12910
+ node
12911
+ end
12912
+
12913
+ def visit_alignof_expression(node)
12914
+ AlignofExpression.new(node.operator, node.operand.accept(self))
12915
+ end
12916
+
12917
+ def visit_alignof_type_expression(node)
12918
+ node
12919
+ end
12920
+
12921
+ def visit_cast_expression(node)
12922
+ CastExpression.new(node.type_name, node.operand.accept(self))
12923
+ end
12924
+
12925
+ def visit_multiplicative_expression(node)
12926
+ MultiplicativeExpression.new(node.operator,
12927
+ node.lhs_operand.accept(self),
12928
+ node.rhs_operand.accept(self))
12929
+ end
12930
+
12931
+ def visit_additive_expression(node)
12932
+ AdditiveExpression.new(node.operator,
12933
+ node.lhs_operand.accept(self),
12934
+ node.rhs_operand.accept(self))
12935
+ end
12936
+
12937
+ def visit_shift_expression(node)
12938
+ ShiftExpression.new(node.operator,
12939
+ node.lhs_operand.accept(self),
12940
+ node.rhs_operand.accept(self))
12941
+ end
12942
+
12943
+ def visit_relational_expression(node)
12944
+ RelationalExpression.new(node.operator,
12945
+ node.lhs_operand.accept(self),
12946
+ node.rhs_operand.accept(self))
12947
+ end
12948
+
12949
+ def visit_equality_expression(node)
12950
+ EqualityExpression.new(node.operator,
12951
+ node.lhs_operand.accept(self),
12952
+ node.rhs_operand.accept(self))
12953
+ end
12954
+
12955
+ def visit_and_expression(node)
12956
+ AndExpression.new(node.operator,
12957
+ node.lhs_operand.accept(self),
12958
+ node.rhs_operand.accept(self))
12959
+ end
12960
+
12961
+ def visit_exclusive_or_expression(node)
12962
+ ExclusiveOrExpression.new(node.operator,
12963
+ node.lhs_operand.accept(self),
12964
+ node.rhs_operand.accept(self))
12965
+ end
12966
+
12967
+ def visit_inclusive_or_expression(node)
12968
+ InclusiveOrExpression.new(node.operator,
12969
+ node.lhs_operand.accept(self),
12970
+ node.rhs_operand.accept(self))
12971
+ end
12972
+
12973
+ def visit_logical_and_expression(node)
12974
+ LogicalAndExpression.new(node.operator,
12975
+ node.lhs_operand.accept(self),
12976
+ node.rhs_operand.accept(self))
12977
+ end
12978
+
12979
+ def visit_logical_or_expression(node)
12980
+ LogicalOrExpression.new(node.operator,
12981
+ node.lhs_operand.accept(self),
12982
+ node.rhs_operand.accept(self))
12983
+ end
12984
+
12985
+ def visit_conditional_expression(node)
12986
+ ConditionalExpression.new(node.condition.accept(self),
12987
+ node.then_expression.accept(self),
12988
+ node.else_expression.accept(self),
12989
+ Token.new("?", "?", node.location))
12990
+ end
12991
+
12992
+ def visit_simple_assignment_expression(node)
12993
+ SimpleAssignmentExpression.new(node.operator,
12994
+ node.lhs_operand.accept(self),
12995
+ node.rhs_operand.accept(self))
12996
+ end
12997
+
12998
+ def visit_compound_assignment_expression(node)
12999
+ CompoundAssignmentExpression.new(node.operator,
13000
+ node.lhs_operand.accept(self),
13001
+ node.rhs_operand.accept(self))
13002
+ end
13003
+
13004
+ def visit_comma_separated_expression(node)
13005
+ expressions = node.expressions.map { |expr| expr.accept(self) }
13006
+ result = CommaSeparatedExpression.new(expressions.shift)
13007
+ expressions.each { |expr| result.expressions.push(expr) }
13008
+ result
13009
+ end
13010
+
13011
+ def visit_initializer(node)
13012
+ case
13013
+ when node.expression
13014
+ Initializer.new(node.expression.accept(self), nil)
13015
+ when node.initializers
13016
+ Initializer.new(nil, node.initializers.map { |i| i.accept(self) })
13017
+ end
13018
+ end
13019
+ end
13020
+ private_constant :ExpressionTransformer
12341
13021
  end
12342
13022
 
12343
13023
  class W0708 < PassiveMessageDetection
@@ -12469,6 +13149,31 @@ module C #:nodoc:
12469
13149
  private_constant :Visitor
12470
13150
  end
12471
13151
 
13152
+ class W0719 < PassiveMessageDetection
13153
+ def initialize(context)
13154
+ super
13155
+ interp = context[:c_interpreter]
13156
+ interp.on_shift_expr_evaled += method(:check)
13157
+ @enum_tbl = interp.environment.enumerator_table
13158
+ end
13159
+
13160
+ private
13161
+ def check(expr, lhs_variable, rhs_variable, result_variable)
13162
+ if expr.rhs_operand.constant?(@enum_tbl)
13163
+ underlying_type = lhs_variable.type
13164
+ underlying_bit_size = ScalarValue.of(underlying_type.bit_size)
13165
+ promoted_type = lhs_variable.type.integer_promoted_type
13166
+ promoted_bit_size = ScalarValue.of(promoted_type.bit_size)
13167
+
13168
+ if rhs_variable.value.must_be_equal_to?(underlying_bit_size) ||
13169
+ rhs_variable.value.must_be_greater_than?(underlying_bit_size) and
13170
+ rhs_variable.value.must_be_less_than?(promoted_bit_size)
13171
+ W(:W0719, expr.location)
13172
+ end
13173
+ end
13174
+ end
13175
+ end
13176
+
12472
13177
  class W0720 < PassiveMessageDetection
12473
13178
  def initialize(context)
12474
13179
  super
@@ -13042,29 +13747,314 @@ module C #:nodoc:
13042
13747
  end
13043
13748
 
13044
13749
  class W0745 < PassiveMessageDetection
13750
+ include SyntaxNodeCollector
13751
+ include InterpreterOptions
13752
+
13045
13753
  def initialize(context)
13046
13754
  super
13047
- interp = context[:c_interpreter]
13048
- interp.on_array_subscript_expr_evaled += method(:check)
13755
+ @interp = context[:c_interpreter]
13756
+ @interp.on_array_subscript_expr_evaled += method(:check_array_subscript)
13757
+ @interp.on_indirection_expr_evaled += method(:check_indirection)
13758
+ @enum_tbl = @interp.environment.enumerator_table
13049
13759
  end
13050
13760
 
13051
- private
13052
- def check(array_subscript_expression,
13053
- array_or_pointer_variable, subscript_variable,
13054
- array_variable, result_variable)
13055
- return unless array_variable && array_variable.type.length
13056
- return unless subscript_variable.value.scalar?
13761
+ private
13762
+ def check_array_subscript(expression, *, subscript, array, result)
13763
+ return unless array
13764
+
13765
+ unless expression.array_subscript.constant?(@enum_tbl)
13766
+ warn_array_oob_access(expression.array_subscript, array, subscript)
13767
+ end
13768
+ end
13769
+
13770
+ def check_indirection(expression, *)
13771
+ array, subscript_expr = extract_array_and_subscript(expression.operand)
13772
+ return unless array
13773
+
13774
+ unless subscript_expr.constant?(@enum_tbl)
13775
+ subscript = @interp.interpret(subscript_expr,
13776
+ QUIET_WITHOUT_SIDE_EFFECT)
13777
+ warn_array_oob_access(expression.operand, array, subscript)
13778
+ end
13779
+ end
13780
+
13781
+ def warn_array_oob_access(expression, array, subscript)
13782
+ if array_length = array.type.length
13783
+ lower_bound = ScalarValue.of(0)
13784
+ upper_bound = ScalarValue.of(array_length - 1)
13785
+
13786
+ lower_test = subscript.value < lower_bound
13787
+ upper_test = subscript.value > upper_bound
13788
+
13789
+ if lower_test.must_be_true? || upper_test.must_be_true?
13790
+ W(:W0745, expression.location)
13791
+ end
13792
+ end
13793
+ end
13794
+
13795
+ def extract_array_and_subscript(expression)
13796
+ array, array_name = extract_array(expression)
13797
+ subscript_expr = create_subscript_expr(expression, array_name)
13798
+ return array, subscript_expr
13799
+ end
13800
+
13801
+ def extract_array(expression)
13802
+ collect_object_specifiers(expression).each do |os|
13803
+ if object = @interp.variable_named(os.identifier.value)
13804
+ case
13805
+ when object.type.array?
13806
+ return object, os.identifier.value
13807
+ when object.type.pointer?
13808
+ if object = @interp.pointee_of(object) and object.type.array?
13809
+ return object, os.identifier.value
13810
+ end
13811
+ end
13812
+ end
13813
+ end
13814
+ nil
13815
+ end
13816
+
13817
+ def create_subscript_expr(expression, array_name)
13818
+ expression.accept(ExpressionTransformer.new(array_name))
13819
+ end
13820
+
13821
+ class ExpressionTransformer < SyntaxTreeVisitor
13822
+ def initialize(array_name)
13823
+ @array_name = array_name
13824
+ end
13825
+
13826
+ def visit_error_expression(node)
13827
+ node
13828
+ end
13829
+
13830
+ def visit_object_specifier(node)
13831
+ if node.identifier.value == @array_name
13832
+ ConstantSpecifier.new(Token.new(:CONSTANT, "0", node.location))
13833
+ else
13834
+ node
13835
+ end
13836
+ end
13837
+
13838
+ def visit_constant_specifier(node)
13839
+ node
13840
+ end
13841
+
13842
+ def visit_string_literal_specifier(node)
13843
+ node
13844
+ end
13845
+
13846
+ def visit_null_constant_specifier(node)
13847
+ node
13848
+ end
13849
+
13850
+ def visit_grouped_expression(node)
13851
+ GroupedExpression.new(node.expression.accept(self))
13852
+ end
13853
+
13854
+ def visit_array_subscript_expression(node)
13855
+ ArraySubscriptExpression.new(node.expression.accept(self),
13856
+ node.array_subscript.accept(self),
13857
+ node.operator)
13858
+ end
13859
+
13860
+ def visit_function_call_expression(node)
13861
+ FunctionCallExpression.new(
13862
+ node.expression.accept(self),
13863
+ node.argument_expressions.map { |expr| expr.accept(self) },
13864
+ node.operator)
13865
+ end
13866
+
13867
+ def visit_member_access_by_value_expression(node)
13868
+ MemberAccessByValueExpression.new(node.expression.accept(self),
13869
+ node.identifier, node.operator)
13870
+ end
13871
+
13872
+ def visit_member_access_by_pointer_expression(node)
13873
+ MemberAccessByPointerExpression.new(node.expression.accept(self),
13874
+ node.identifier, node.operator)
13875
+ end
13876
+
13877
+ def visit_bit_access_by_value_expression(node)
13878
+ BitAccessByValueExpression.new(node.expression.accept(self),
13879
+ node.constant, node.operator)
13880
+ end
13881
+
13882
+ def visit_bit_access_by_pointer_expression(node)
13883
+ BitAccessByPointerExpression.new(node.expression.accept(self),
13884
+ node.constant, node.operator)
13885
+ end
13886
+
13887
+ def visit_postfix_increment_expression(node)
13888
+ # NOTE: The postfix-increment-expression is already evaluated with
13889
+ # side-effect.
13890
+ # To rollback the side-effect, create an inverted expression.
13891
+ PrefixDecrementExpression.new(
13892
+ Token.new("--", "--", node.operator.location),
13893
+ node.operand.accept(self))
13894
+ end
13895
+
13896
+ def visit_postfix_decrement_expression(node)
13897
+ # NOTE: The postfix-decrement-expression is already evaluated with
13898
+ # side-effect.
13899
+ # To rollback the side-effect, create an inverted expression.
13900
+ PrefixIncrementExpression.new(
13901
+ Token.new("++", "++", node.operator.location),
13902
+ node.operand.accept(self))
13903
+ end
13904
+
13905
+ def visit_compound_literal_expression(node)
13906
+ CompoundLiteralExpression.new(
13907
+ node.type_name, node.initializers.map { |ini| ini.accept(self) },
13908
+ node.operator)
13909
+ end
13910
+
13911
+ def visit_prefix_increment_expression(node)
13912
+ # NOTE: The prefix-increment-expression is already evaluated with
13913
+ # side-effect.
13914
+ # To rollback the side-effect, create an inverted expression.
13915
+ PostfixDecrementExpression.new(
13916
+ Token.new("--", "--", node.operator.location),
13917
+ node.operand.accept(self))
13918
+ end
13919
+
13920
+ def visit_prefix_decrement_expression(node)
13921
+ # NOTE: The prefix-decrement-expression is already evaluated with
13922
+ # side-effect.
13923
+ # To rollback the side-effect, create an inverted expression.
13924
+ PostfixIncrementExpression.new(
13925
+ Token.new("++", "++", node.operator.location),
13926
+ node.operand.accept(self))
13927
+ end
13928
+
13929
+ def visit_address_expression(node)
13930
+ AddressExpression.new(node.operator, node.operand.accept(self))
13931
+ end
13932
+
13933
+ def visit_indirection_expression(node)
13934
+ IndirectionExpression.new(node.operator, node.operand.accept(self))
13935
+ end
13936
+
13937
+ def visit_unary_arithmetic_expression(node)
13938
+ UnaryArithmeticExpression.new(node.operator, node.operand.accept(self))
13939
+ end
13940
+
13941
+ def visit_sizeof_expression(node)
13942
+ SizeofExpression.new(node.operator, node.operand.accept(self))
13943
+ end
13944
+
13945
+ def visit_sizeof_type_expression(node)
13946
+ node
13947
+ end
13948
+
13949
+ def visit_alignof_expression(node)
13950
+ AlignofExpression.new(node.operator, node.operand.accept(self))
13951
+ end
13952
+
13953
+ def visit_alignof_type_expression(node)
13954
+ node
13955
+ end
13956
+
13957
+ def visit_cast_expression(node)
13958
+ CastExpression.new(node.type_name, node.operand.accept(self))
13959
+ end
13960
+
13961
+ def visit_multiplicative_expression(node)
13962
+ MultiplicativeExpression.new(node.operator,
13963
+ node.lhs_operand.accept(self),
13964
+ node.rhs_operand.accept(self))
13965
+ end
13966
+
13967
+ def visit_additive_expression(node)
13968
+ AdditiveExpression.new(node.operator,
13969
+ node.lhs_operand.accept(self),
13970
+ node.rhs_operand.accept(self))
13971
+ end
13972
+
13973
+ def visit_shift_expression(node)
13974
+ ShiftExpression.new(node.operator,
13975
+ node.lhs_operand.accept(self),
13976
+ node.rhs_operand.accept(self))
13977
+ end
13978
+
13979
+ def visit_relational_expression(node)
13980
+ RelationalExpression.new(node.operator,
13981
+ node.lhs_operand.accept(self),
13982
+ node.rhs_operand.accept(self))
13983
+ end
13984
+
13985
+ def visit_equality_expression(node)
13986
+ EqualityExpression.new(node.operator,
13987
+ node.lhs_operand.accept(self),
13988
+ node.rhs_operand.accept(self))
13989
+ end
13990
+
13991
+ def visit_and_expression(node)
13992
+ AndExpression.new(node.operator,
13993
+ node.lhs_operand.accept(self),
13994
+ node.rhs_operand.accept(self))
13995
+ end
13996
+
13997
+ def visit_exclusive_or_expression(node)
13998
+ ExclusiveOrExpression.new(node.operator,
13999
+ node.lhs_operand.accept(self),
14000
+ node.rhs_operand.accept(self))
14001
+ end
14002
+
14003
+ def visit_inclusive_or_expression(node)
14004
+ InclusiveOrExpression.new(node.operator,
14005
+ node.lhs_operand.accept(self),
14006
+ node.rhs_operand.accept(self))
14007
+ end
14008
+
14009
+ def visit_logical_and_expression(node)
14010
+ LogicalAndExpression.new(node.operator,
14011
+ node.lhs_operand.accept(self),
14012
+ node.rhs_operand.accept(self))
14013
+ end
14014
+
14015
+ def visit_logical_or_expression(node)
14016
+ LogicalOrExpression.new(node.operator,
14017
+ node.lhs_operand.accept(self),
14018
+ node.rhs_operand.accept(self))
14019
+ end
14020
+
14021
+ def visit_conditional_expression(node)
14022
+ ConditionalExpression.new(node.condition.accept(self),
14023
+ node.then_expression.accept(self),
14024
+ node.else_expression.accept(self),
14025
+ Token.new("?", "?", node.location))
14026
+
14027
+ end
14028
+
14029
+ def visit_simple_assignment_expression(node)
14030
+ SimpleAssignmentExpression.new(node.operator,
14031
+ node.lhs_operand.accept(self),
14032
+ node.rhs_operand.accept(self))
14033
+ end
13057
14034
 
13058
- lower_bound = ScalarValue.of(0)
13059
- upper_bound = ScalarValue.of(array_variable.type.length - 1)
14035
+ def visit_compound_assignment_expression(node)
14036
+ CompoundAssignmentExpression.new(node.operator,
14037
+ node.lhs_operand.accept(self),
14038
+ node.rhs_operand.accept(self))
14039
+ end
13060
14040
 
13061
- lower_test = subscript_variable.value < lower_bound
13062
- upper_test = subscript_variable.value > upper_bound
14041
+ def visit_comma_separated_expression(node)
14042
+ expressions = node.expressions.map { |expr| expr.accept(self) }
14043
+ result = CommaSeparatedExpression.new(expressions.shift)
14044
+ expressions.each { |expr| result.expressions.push(expr) }
14045
+ result
14046
+ end
13063
14047
 
13064
- if lower_test.must_be_true? || upper_test.must_be_true?
13065
- W(:W0745, array_subscript_expression.array_subscript.location)
14048
+ def visit_initializer(node)
14049
+ case
14050
+ when node.expression
14051
+ Initializer.new(node.expression.accept(self), nil)
14052
+ when node.initializers
14053
+ Initializer.new(nil, node.initializers.map { |i| i.accept(self) })
14054
+ end
13066
14055
  end
13067
14056
  end
14057
+ private_constant :ExpressionTransformer
13068
14058
  end
13069
14059
 
13070
14060
  class W0747 < W0119
@@ -13481,6 +14471,54 @@ module C #:nodoc:
13481
14471
  end
13482
14472
  end
13483
14473
 
14474
+ class W0780 < PassiveMessageDetection
14475
+ def initialize(context)
14476
+ super
14477
+ interp = context[:c_interpreter]
14478
+ interp.on_shift_expr_evaled += method(:check)
14479
+ @enum_tbl = interp.environment.enumerator_table
14480
+ end
14481
+
14482
+ private
14483
+ def check(expression, lhs_variable, rhs_variable, result_variable)
14484
+ operator = expression.operator.type
14485
+ return unless operator == "<<" || operator == "<<="
14486
+
14487
+ if lhs_variable.type.unsigned? && expression.constant?(@enum_tbl)
14488
+ if must_overflow?(lhs_variable, rhs_variable)
14489
+ W(:W0780, expression.location)
14490
+ end
14491
+ end
14492
+ end
14493
+
14494
+ def must_overflow?(lhs_variable, rhs_variable)
14495
+ result = lhs_variable.value << rhs_variable.value
14496
+ result.must_be_greater_than?(ScalarValue.of(lhs_variable.type.max_value))
14497
+ end
14498
+ end
14499
+
14500
+ class W0783 < PassiveMessageDetection
14501
+ def initialize(context)
14502
+ super
14503
+ interp = context[:c_interpreter]
14504
+ interp.on_explicit_conv_performed += method(:check)
14505
+ end
14506
+
14507
+ private
14508
+ def check(expression, original_variable, result_variable)
14509
+ from_type = original_variable.type.unqualify
14510
+ to_type = result_variable.type.unqualify
14511
+
14512
+ return unless from_type.pointer? && to_type.pointer?
14513
+
14514
+ unless from_type.base_type.void? || to_type.base_type.void?
14515
+ if from_type.base_type.incomplete? || to_type.base_type.incomplete?
14516
+ W(:W0783, expression.location)
14517
+ end
14518
+ end
14519
+ end
14520
+ end
14521
+
13484
14522
  class W0785 < PassiveMessageDetection
13485
14523
  def initialize(context)
13486
14524
  super
@@ -14010,6 +15048,73 @@ module C #:nodoc:
14010
15048
  end
14011
15049
  end
14012
15050
 
15051
+ class W0792 < PassiveMessageDetection
15052
+ def initialize(context)
15053
+ super
15054
+ interp = context[:c_interpreter]
15055
+ interp.on_explicit_conv_performed += method(:check)
15056
+ end
15057
+
15058
+ private
15059
+ def check(expression, original_variable, result_variable)
15060
+ lhs_type = original_variable.type.unqualify
15061
+ rhs_type = result_variable.type.unqualify
15062
+
15063
+ case
15064
+ when lhs_type.floating? &&
15065
+ rhs_type.pointer? && rhs_type.base_type.function?
15066
+ W(:W0792, expression.location)
15067
+ when rhs_type.floating? &&
15068
+ lhs_type.pointer? && lhs_type.base_type.function?
15069
+ W(:W0792, expression.location)
15070
+ end
15071
+ end
15072
+ end
15073
+
15074
+ class W0793 < PassiveMessageDetection
15075
+ def initialize(context)
15076
+ super
15077
+ interp = context[:c_interpreter]
15078
+ interp.on_explicit_conv_performed += method(:check)
15079
+ end
15080
+
15081
+ private
15082
+ def check(expression, original_variable, result_variable)
15083
+ lhs_type = original_variable.type.unqualify
15084
+ rhs_type = result_variable.type.unqualify
15085
+
15086
+ if lhs_type.pointer? && rhs_type.pointer?
15087
+ case
15088
+ when lhs_type.base_type.void? || rhs_type.base_type.void?
15089
+ # NOTE: Nothing to be done with conversion between `void *' and any
15090
+ # pointer and between `void *' and `void *'.
15091
+ when lhs_type.base_type.function? && !rhs_type.base_type.function?,
15092
+ rhs_type.base_type.function? && !lhs_type.base_type.function?
15093
+ W(:W0793, expression.location)
15094
+ end
15095
+ end
15096
+ end
15097
+ end
15098
+
15099
+ class W0794 < PassiveMessageDetection
15100
+ def initialize(context)
15101
+ super
15102
+ interp = context[:c_interpreter]
15103
+ interp.on_shift_expr_evaled += method(:check)
15104
+ @enum_tbl = interp.environment.enumerator_table
15105
+ end
15106
+
15107
+ private
15108
+ def check(expression, lhs_variable, *)
15109
+ case expression.operator.type
15110
+ when "<<", "<<="
15111
+ unless expression.lhs_operand.constant?(@enum_tbl)
15112
+ W(:W0794, expression.location) if lhs_variable.type.signed?
15113
+ end
15114
+ end
15115
+ end
15116
+ end
15117
+
14013
15118
  class W0795 < PassiveMessageDetection
14014
15119
  def initialize(context)
14015
15120
  super
@@ -14312,6 +15417,133 @@ module C #:nodoc:
14312
15417
  end
14313
15418
  end
14314
15419
 
15420
+ class W0830 < PassiveMessageDetection
15421
+ def initialize(context)
15422
+ super
15423
+ visitor = context[:c_visitor]
15424
+ visitor.enter_enum_specifier += method(:check)
15425
+ end
15426
+
15427
+ private
15428
+ def check(enum_specifier)
15429
+ if extra_comma = enum_specifier.trailing_comma
15430
+ W(:W0830, extra_comma.location)
15431
+ end
15432
+ end
15433
+ end
15434
+
15435
+ class W0833 < PassiveMessageDetection
15436
+ def initialize(context)
15437
+ super
15438
+ interp = context[:c_interpreter]
15439
+ interp.on_constant_referred += method(:check)
15440
+ end
15441
+
15442
+ private
15443
+ def check(constant_specifier, *)
15444
+ if constant_specifier.constant.value =~ /LL/i
15445
+ W(:W0833, constant_specifier.location)
15446
+ end
15447
+ end
15448
+ end
15449
+
15450
+ class W0834 < PassiveMessageDetection
15451
+ # NOTE: W0834 may be duplicative on a function-definition because
15452
+ # function-definition has both parameter-declarations and
15453
+ # parameter-definitions.
15454
+ ensure_uniqueness_of :W0834
15455
+
15456
+ def initialize(context)
15457
+ super
15458
+ visitor = context[:c_visitor]
15459
+ visitor.enter_member_declaration += method(:check_member_decl)
15460
+ visitor.enter_typedef_declaration += method(:check_declspec_holder)
15461
+ visitor.enter_function_declaration += method(:check_declspec_holder)
15462
+ visitor.enter_parameter_declaration += method(:check_declspec_holder)
15463
+ visitor.enter_variable_declaration += method(:check_declspec_holder)
15464
+ visitor.enter_variable_definition += method(:check_declspec_holder)
15465
+ visitor.enter_ansi_function_definition += method(:check_declspec_holder)
15466
+ visitor.enter_kandr_function_definition += method(:check_declspec_holder)
15467
+ visitor.enter_parameter_definition += method(:check_declspec_holder)
15468
+ visitor.enter_type_name += method(:check_type_name)
15469
+ end
15470
+
15471
+ private
15472
+ def check_member_decl(node)
15473
+ type_specifiers = node.specifier_qualifier_list.type_specifiers
15474
+ if first_ts = type_specifiers.first
15475
+ node.type.accept(Visitor.new(@context, first_ts.location))
15476
+ end
15477
+ end
15478
+
15479
+ def check_type_name(node)
15480
+ type_specifiers = node.specifier_qualifier_list.type_specifiers
15481
+ if first_ts = type_specifiers.first
15482
+ node.type.accept(Visitor.new(@context, first_ts.location))
15483
+ end
15484
+ end
15485
+
15486
+ def check_declspec_holder(declspec_holder)
15487
+ type_specifiers = declspec_holder.type_specifiers
15488
+ if first_ts = type_specifiers.first
15489
+ declspec_holder.type.accept(Visitor.new(@context, first_ts.location))
15490
+ end
15491
+ end
15492
+
15493
+ class Visitor < TypeVisitor
15494
+ include ReportUtil
15495
+
15496
+ def initialize(context, location)
15497
+ @context = context
15498
+ @location = location
15499
+ end
15500
+
15501
+ def visit_long_long_type(type)
15502
+ warn
15503
+ end
15504
+
15505
+ def visit_signed_long_long_type(type)
15506
+ warn
15507
+ end
15508
+
15509
+ def visit_unsigned_long_long_type(type)
15510
+ warn
15511
+ end
15512
+
15513
+ def visit_long_long_int_type(type)
15514
+ warn
15515
+ end
15516
+
15517
+ def visit_signed_long_long_int_type(type)
15518
+ warn
15519
+ end
15520
+
15521
+ def visit_unsigned_long_long_int_type(type)
15522
+ warn
15523
+ end
15524
+
15525
+ def visit_function_type(type)
15526
+ type.return_type.accept(self)
15527
+ end
15528
+
15529
+ def visit_struct_type(type)
15530
+ end
15531
+
15532
+ def visit_union_type(type)
15533
+ end
15534
+
15535
+ private
15536
+ def warn
15537
+ W(:W0834, @location)
15538
+ end
15539
+
15540
+ def report
15541
+ @context.report
15542
+ end
15543
+ end
15544
+ private_constant :Visitor
15545
+ end
15546
+
14315
15547
  class W0947 < PassiveMessageDetection
14316
15548
  def initialize(context)
14317
15549
  super
@@ -14401,6 +15633,24 @@ module C #:nodoc:
14401
15633
  end
14402
15634
  end
14403
15635
 
15636
+ class W1026 < PassiveMessageDetection
15637
+ def initialize(context)
15638
+ super
15639
+ interp = context[:c_interpreter]
15640
+ interp.on_function_call_expr_evaled += method(:check)
15641
+ end
15642
+
15643
+ private
15644
+ def check(expression, function, arg_variables, *)
15645
+ arg_exprs = expression.argument_expressions
15646
+ arg_exprs.zip(arg_variables).each_with_index do |(expr, var), index|
15647
+ if var.type.incomplete?
15648
+ W(:W1026, expr.location, index + 1)
15649
+ end
15650
+ end
15651
+ end
15652
+ end
15653
+
14404
15654
  class W1027 < PassiveMessageDetection
14405
15655
  def initialize(context)
14406
15656
  super
@@ -14584,6 +15834,103 @@ module C #:nodoc:
14584
15834
  end
14585
15835
  end
14586
15836
 
15837
+ class W1039 < PassiveMessageDetection
15838
+ def initialize(context)
15839
+ super
15840
+ interp = context[:c_interpreter]
15841
+ interp.on_function_call_expr_evaled += method(:check)
15842
+ @environ = interp.environment
15843
+ end
15844
+
15845
+ private
15846
+ def check(expression, function, arg_variables, *)
15847
+ if function.named?
15848
+ case function.name
15849
+ when /\A.*printf\z/
15850
+ check_printf_format(expression, arg_variables)
15851
+ when /\A.*scanf\z/
15852
+ check_scanf_format(expression, arg_variables)
15853
+ end
15854
+ end
15855
+ end
15856
+
15857
+ def check_printf_format(expression, arg_variables)
15858
+ if format = create_printf_format(expression, arg_variables)
15859
+ css = format.conversion_specifiers
15860
+ css.each_with_index do |cs, index|
15861
+ if cs.length_modifier == "ll"
15862
+ W(:W1039, format.location, index + 1)
15863
+ end
15864
+ end
15865
+ end
15866
+ end
15867
+
15868
+ def check_scanf_format(expression, arg_variables)
15869
+ if format = create_scanf_format(expression, arg_variables)
15870
+ css = format.conversion_specifiers
15871
+ css.each_with_index do |cs, index|
15872
+ if cs.length_modifier == "ll"
15873
+ W(:W1039, format.location, index + 1)
15874
+ end
15875
+ end
15876
+ end
15877
+ end
15878
+
15879
+ def create_printf_format(expression, arg_variables)
15880
+ if format_index = format_arg_index_of(expression)
15881
+ format_arg = expression.argument_expressions[format_index]
15882
+ if format_arg && format_arg.literal.value =~ /\AL?"(.*)"\z/i
15883
+ location = format_arg.location
15884
+ trailing_args = arg_variables[(format_index + 1)..-1] || []
15885
+ return PrintfFormat.new($1, location, trailing_args, @environ)
15886
+ end
15887
+ end
15888
+ nil
15889
+ end
15890
+
15891
+ def create_scanf_format(expression, arg_variables)
15892
+ if format_index = format_arg_index_of(expression)
15893
+ format_arg = expression.argument_expressions[format_index]
15894
+ if format_arg && format_arg.literal.value =~ /\AL?"(.*)"\z/i
15895
+ location = format_arg.location
15896
+ trailing_args = arg_variables[(format_index + 1)..-1] || []
15897
+ return ScanfFormat.new($1, location, trailing_args, @environ)
15898
+ end
15899
+ end
15900
+ nil
15901
+ end
15902
+
15903
+ def format_arg_index_of(function_call_expression)
15904
+ function_call_expression.argument_expressions.index do |arg_expr|
15905
+ arg_expr.kind_of?(StringLiteralSpecifier)
15906
+ end
15907
+ end
15908
+ end
15909
+
15910
+ class W1047 < PassiveMessageDetection
15911
+ include SyntaxNodeCollector
15912
+
15913
+ def initialize(context)
15914
+ super
15915
+ interp = context[:c_interpreter]
15916
+ interp.on_variable_initialized += method(:check)
15917
+ @enum_tbl = interp.environment.enumerator_table
15918
+ end
15919
+
15920
+ private
15921
+ def check(variable_definition, *)
15922
+ type = variable_definition.type
15923
+ if type.struct? || type.union? || type.array?
15924
+ if initializer = variable_definition.initializer
15925
+ obj_specs = collect_object_specifiers(initializer)
15926
+ if obj_specs.any? { |os| !@enum_tbl.lookup(os.identifier.value) }
15927
+ W(:W1047, variable_definition.location)
15928
+ end
15929
+ end
15930
+ end
15931
+ end
15932
+ end
15933
+
14587
15934
  class W1049 < PassiveMessageDetection
14588
15935
  def initialize(context)
14589
15936
  super
@@ -15025,6 +16372,36 @@ module C #:nodoc:
15025
16372
  end
15026
16373
  end
15027
16374
 
16375
+ class W1071 < PassiveMessageDetection
16376
+ def initialize(context)
16377
+ super
16378
+ interp = context[:c_interpreter]
16379
+ interp.on_function_started += method(:enter_function)
16380
+ interp.on_function_ended += method(:leave_function)
16381
+ interp.on_implicit_return_evaled += method(:memorize_termination)
16382
+ interp.on_return_stmt_evaled += lambda { |*| memorize_termination }
16383
+ @current_function = nil
16384
+ end
16385
+
16386
+ private
16387
+ def enter_function(function_definition, *)
16388
+ @current_function = function_definition
16389
+ @termination_points = 0
16390
+ end
16391
+
16392
+ def leave_function(*)
16393
+ if @current_function && @termination_points > 1
16394
+ W(:W1071,
16395
+ @current_function.location, @current_function.identifier.value)
16396
+ end
16397
+ @current_function = nil
16398
+ end
16399
+
16400
+ def memorize_termination
16401
+ @termination_points += 1 if @current_function
16402
+ end
16403
+ end
16404
+
15028
16405
  class W1073 < PassiveMessageDetection
15029
16406
  def initialize(context)
15030
16407
  super
@@ -15228,6 +16605,11 @@ module C #:nodoc:
15228
16605
  from_type = original_variable.type
15229
16606
  to_type = result_variable.type
15230
16607
 
16608
+ if from_type.undeclared? || from_type.unresolved? ||
16609
+ to_type.undeclared? || to_type.unresolved?
16610
+ return
16611
+ end
16612
+
15231
16613
  unless from_type.standard? && to_type.standard?
15232
16614
  unless from_type.convertible?(to_type)
15233
16615
  W(:W9003, initializer_or_expression.location, from_type.brief_image)