adlint 1.16.0 → 1.18.0

Sign up to get free protection for your applications and to get access to all the features.
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)