|
25 | 25 | import static au.com.integradev.delphi.symbol.resolve.EqualityType.EQUAL;
|
26 | 26 | import static au.com.integradev.delphi.symbol.resolve.EqualityType.EXACT;
|
27 | 27 | import static au.com.integradev.delphi.symbol.resolve.EqualityType.INCOMPATIBLE_TYPES;
|
28 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.ANSISTRING; |
29 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.BYTE; |
30 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.CARDINAL; |
31 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.DOUBLE_CURRENCY; |
32 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.DYNAMIC_ARRAY; |
33 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.ENUM; |
34 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.EXTENDED; |
35 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.FORMAL_BOOLEAN; |
36 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.INCOMPATIBLE_VARIANT; |
37 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.INTEGER; |
38 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.NO_CONVERSION_REQUIRED; |
39 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.SHORTINT; |
40 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.SHORTSTRING; |
41 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.SINGLE; |
42 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.SMALLINT; |
43 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.UNICODESTRING; |
44 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.WIDESTRING; |
45 |
| -import static au.com.integradev.delphi.symbol.resolve.VariantConversionType.WORD; |
46 | 28 | import static java.lang.Math.abs;
|
47 | 29 | import static java.util.function.Predicate.not;
|
48 | 30 |
|
|
52 | 34 | import com.google.common.collect.ComparisonChain;
|
53 | 35 | import java.util.ArrayList;
|
54 | 36 | import java.util.Collections;
|
| 37 | +import java.util.Comparator; |
55 | 38 | import java.util.List;
|
| 39 | +import java.util.Objects; |
56 | 40 | import java.util.Set;
|
57 | 41 | import java.util.stream.Collectors;
|
58 | 42 | import org.sonar.plugins.communitydelphi.api.type.CodePages;
|
@@ -192,7 +176,7 @@ private static void processArgument(
|
192 | 176 | }
|
193 | 177 |
|
194 | 178 | checkCodePageDistance(candidate, argumentType, parameterType);
|
195 |
| - checkVariantConversions(candidate, argumentType, parameterType); |
| 179 | + addVariantConversion(candidate, argumentType, parameterType); |
196 | 180 |
|
197 | 181 | // When an ambiguous procedural type was changed to an invocation, an exact match is
|
198 | 182 | // downgraded to equal.
|
@@ -352,22 +336,15 @@ private static void checkCodePageDistance(
|
352 | 336 | }
|
353 | 337 | }
|
354 | 338 |
|
355 |
| - // Keep track of implicit variant conversions |
356 |
| - // Also invalidate candidates that would produce invalid variant conversions |
357 |
| - private static void checkVariantConversions( |
| 339 | + private static void addVariantConversion( |
358 | 340 | InvocationCandidate candidate, Type argumentType, Type parameterType) {
|
359 |
| - argumentType = TypeUtils.findBaseType(argumentType); |
360 |
| - parameterType = TypeUtils.findBaseType(parameterType); |
361 |
| - VariantConversionType variantConversionType = NO_CONVERSION_REQUIRED; |
362 |
| - if (argumentType.isVariant()) { |
363 |
| - variantConversionType = VariantConversionType.fromType(parameterType); |
364 |
| - if (variantConversionType == INCOMPATIBLE_VARIANT) { |
365 |
| - candidate.setInvalid(); |
366 |
| - } |
367 |
| - } else if (parameterType.isVariant()) { |
368 |
| - variantConversionType = VariantConversionType.fromType(argumentType); |
| 341 | + if (argumentType.isVariant() && !parameterType.isVariant()) { |
| 342 | + candidate.addVariantConversion(parameterType); |
| 343 | + } else if (parameterType.isVariant() && !argumentType.isVariant()) { |
| 344 | + candidate.addVariantConversion(argumentType); |
| 345 | + } else { |
| 346 | + candidate.addVariantConversion(null); |
369 | 347 | }
|
370 |
| - candidate.addVariantConversion(variantConversionType); |
371 | 348 | }
|
372 | 349 |
|
373 | 350 | private static EqualityType varParameterAllowed(Type argType, Parameter parameter) {
|
@@ -537,117 +514,102 @@ private int isBetterCandidate(InvocationCandidate candidate, InvocationCandidate
|
537 | 514 | private int getVariantDistance(InvocationCandidate candidate, InvocationCandidate bestCandidate) {
|
538 | 515 | int variantDistance = 0;
|
539 | 516 | for (int i = 0; i < arguments.size(); ++i) {
|
540 |
| - VariantConversionType currentVcl = candidate.getVariantConversionType(i); |
541 |
| - VariantConversionType bestVcl = bestCandidate.getVariantConversionType(i); |
542 |
| - variantDistance += isBetterVariantConversion(currentVcl, bestVcl); |
| 517 | + Type current = candidate.getVariantConversionType(i); |
| 518 | + Type best = bestCandidate.getVariantConversionType(i); |
| 519 | + variantDistance += isBetterVariantConversion(current, best); |
543 | 520 | }
|
544 | 521 | return variantDistance;
|
545 | 522 | }
|
546 | 523 |
|
547 |
| - /** |
548 |
| - * Determines which variant conversion type takes precedence when converting a variant type |
549 |
| - * argument to a parameter type. |
550 |
| - * |
551 |
| - * <p>Delphi precedence rules extracted from test programs: |
552 |
| - * |
553 |
| - * <ul> |
554 |
| - * <li>single > (char, currency, int64, shortstring, ansistring, widestring, unicodestring, |
555 |
| - * extended, double) |
556 |
| - * <li>double/currency > (char, int64, shortstring, ansistring, widestring, unicodestring, |
557 |
| - * extended) |
558 |
| - * <li>extended > (char, int64, shortstring, ansistring, widestring, unicodestring) |
559 |
| - * <li>longint/cardinal > (int64, shortstring, ansistring, widestring, unicodestring, extended, |
560 |
| - * double, single, char, currency) |
561 |
| - * <li>smallint > (longint, int64, shortstring, ansistring, widestring, unicodestring, extended, |
562 |
| - * double single, char, currency); |
563 |
| - * <li>word > (longint, cardinal, int64, shortstring, ansistring, widestring, unicodestring, |
564 |
| - * extended, double single, char, currency); |
565 |
| - * <li>shortint > (longint, smallint, int64, shortstring, ansistring, widestring, unicodestring, |
566 |
| - * extended, double, single, char, currency) |
567 |
| - * <li>byte > (longint, cardinal, word, smallint, int64, shortstring, ansistring, widestring, |
568 |
| - * unicodestring, extended, double, single, char, currency); |
569 |
| - * <li>boolean/formal > (char, int64, shortstring, ansistring, widestring, unicodestring) |
570 |
| - * <li>widestring > (char, int64, shortstring, ansistring, unicodestring) |
571 |
| - * <li>unicodestring > (char, int64, shortstring, ansistring) |
572 |
| - * <li>ansistring > (char, int64, shortstring) |
573 |
| - * <li>shortstring > (char, int64) |
574 |
| - * </ul> |
575 |
| - * |
576 |
| - * <p>Relations not mentioned mean that they conflict: no decision possible |
577 |
| - * |
578 |
| - * @param currentVcl The conversion type we're checking |
579 |
| - * @param bestVcl The best conversion type so far |
580 |
| - * @return |
581 |
| - * <ul> |
582 |
| - * <li>> 0 when currentVcl is better than bestVcl |
583 |
| - * <li>< 0 when bestVcl is better than currentVcl |
584 |
| - * <li>= 0 when both are equal |
585 |
| - * </ul> |
586 |
| - * |
587 |
| - * @see <a href="https://github.com/fpc/FPCSource/blob/main/compiler/htypechk.pas#L3367"> |
588 |
| - * is_better_candidate_single_variant</a> |
589 |
| - */ |
590 |
| - private static int isBetterVariantConversion( |
591 |
| - VariantConversionType currentVcl, VariantConversionType bestVcl) { |
592 |
| - if (currentVcl == bestVcl) { |
| 524 | + private static int isBetterVariantConversion(Type current, Type best) { |
| 525 | + if (current == null && best == null) { |
593 | 526 | return 0;
|
594 |
| - } else if (currentVcl == INCOMPATIBLE_VARIANT || bestVcl == NO_CONVERSION_REQUIRED) { |
595 |
| - return -1; |
596 |
| - } else if (bestVcl == INCOMPATIBLE_VARIANT || currentVcl == NO_CONVERSION_REQUIRED) { |
597 |
| - return 1; |
598 |
| - } else if (currentVcl == FORMAL_BOOLEAN || bestVcl == FORMAL_BOOLEAN) { |
599 |
| - if (currentVcl == FORMAL_BOOLEAN) { |
600 |
| - return VariantConversionType.isChari64Str(bestVcl) ? 1 : 0; |
601 |
| - } else { |
602 |
| - return VariantConversionType.isChari64Str(currentVcl) ? -1 : 0; |
603 |
| - } |
604 |
| - } else if (currentVcl == BYTE || bestVcl == BYTE) { |
605 |
| - return calculateRelation(currentVcl, bestVcl, BYTE, Set.of(SHORTINT)); |
606 |
| - } else if (currentVcl == SHORTINT || bestVcl == SHORTINT) { |
607 |
| - return calculateRelation(currentVcl, bestVcl, SHORTINT, Set.of(WORD, CARDINAL)); |
608 |
| - } else if (currentVcl == WORD || bestVcl == WORD) { |
609 |
| - return calculateRelation(currentVcl, bestVcl, WORD, Set.of(SMALLINT)); |
610 |
| - } else if (currentVcl == SMALLINT || bestVcl == SMALLINT) { |
611 |
| - return calculateRelation(currentVcl, bestVcl, SMALLINT, Set.of(CARDINAL)); |
612 |
| - } else if (currentVcl == CARDINAL || bestVcl == CARDINAL) { |
613 |
| - return calculateRelation(currentVcl, bestVcl, CARDINAL, Set.of(INTEGER)); |
614 |
| - } else if (currentVcl == INTEGER || bestVcl == INTEGER) { |
615 |
| - return (bestVcl == INTEGER) ? -1 : 1; |
616 |
| - } else if (currentVcl == SINGLE || bestVcl == SINGLE) { |
617 |
| - return (bestVcl == SINGLE) ? -1 : 1; |
618 |
| - } else if (currentVcl == DOUBLE_CURRENCY || bestVcl == DOUBLE_CURRENCY) { |
619 |
| - return (bestVcl == DOUBLE_CURRENCY) ? -1 : 1; |
620 |
| - } else if (currentVcl == EXTENDED || bestVcl == EXTENDED) { |
621 |
| - return (bestVcl == EXTENDED) ? -1 : 1; |
622 |
| - } else if (currentVcl == WIDESTRING || bestVcl == WIDESTRING) { |
623 |
| - return (bestVcl == WIDESTRING) ? -1 : 1; |
624 |
| - } else if (currentVcl == UNICODESTRING || bestVcl == UNICODESTRING) { |
625 |
| - return (bestVcl == UNICODESTRING) ? -1 : 1; |
626 |
| - } else if (currentVcl == ANSISTRING || bestVcl == ANSISTRING) { |
627 |
| - return (bestVcl == ANSISTRING) ? -1 : 1; |
628 |
| - } else if (currentVcl == SHORTSTRING || bestVcl == SHORTSTRING) { |
629 |
| - return (bestVcl == SHORTSTRING) ? -1 : 1; |
630 |
| - } else if (currentVcl == ENUM || bestVcl == ENUM) { |
631 |
| - return (bestVcl == ENUM) ? -1 : 1; |
632 |
| - } else if (currentVcl == DYNAMIC_ARRAY || bestVcl == DYNAMIC_ARRAY) { |
633 |
| - return (bestVcl == DYNAMIC_ARRAY) ? -1 : 1; |
634 |
| - } |
635 |
| - |
636 |
| - // All possibilities should have been checked now. |
637 |
| - throw new AssertionError("Unhandled VariantConversionType!"); |
| 527 | + } |
| 528 | + return ComparisonChain.start() |
| 529 | + .compare(current, best, Comparator.comparing(Objects::isNull)) |
| 530 | + .compare(current, best, Comparator.comparing(InvocationResolver::isIInterface)) |
| 531 | + .compare(current, best, Comparator.comparing(Type::isUntyped)) |
| 532 | + .compare(current, best, InvocationResolver::compareNumericType) |
| 533 | + .compare(current, best, InvocationResolver::compareRealSize) |
| 534 | + .compare(current, best, InvocationResolver::compareIntegerRange) |
| 535 | + .compare(current, best, InvocationResolver::compareStringType) |
| 536 | + .result(); |
638 | 537 | }
|
639 | 538 |
|
640 |
| - private static int calculateRelation( |
641 |
| - VariantConversionType currentVcl, |
642 |
| - VariantConversionType bestVcl, |
643 |
| - VariantConversionType testVcl, |
644 |
| - Set<VariantConversionType> conflictTypes) { |
645 |
| - if (conflictTypes.contains(bestVcl) || conflictTypes.contains(currentVcl)) { |
646 |
| - return 0; |
647 |
| - } else if (bestVcl == testVcl) { |
| 539 | + private static boolean isIInterface(Type type) { |
| 540 | + return TypeUtils.findBaseType(type).is("System.IInterface"); |
| 541 | + } |
| 542 | + |
| 543 | + private static int compareNumericType(Type a, Type b) { |
| 544 | + if (a.isReal() && b.isInteger()) { |
| 545 | + return 1; |
| 546 | + } else if (b.isReal() && a.isInteger()) { |
648 | 547 | return -1;
|
649 | 548 | } else {
|
650 |
| - return 1; |
| 549 | + return 0; |
| 550 | + } |
| 551 | + } |
| 552 | + |
| 553 | + private static int compareRealSize(Type a, Type b) { |
| 554 | + if (!a.isReal() || !b.isReal()) { |
| 555 | + return 0; |
| 556 | + } |
| 557 | + if (isCurrencyCompConflict(a, b) || isCurrencyCompConflict(b, a)) { |
| 558 | + return 0; |
| 559 | + } |
| 560 | + return Objects.compare(a, b, Comparator.comparingInt(Type::size)); |
| 561 | + } |
| 562 | + |
| 563 | + private static boolean isCurrencyCompConflict(Type currencyComp, Type real) { |
| 564 | + currencyComp = TypeUtils.findBaseType(currencyComp); |
| 565 | + return (currencyComp.is(IntrinsicType.CURRENCY) || currencyComp.is(IntrinsicType.COMP)) |
| 566 | + && real.isReal() |
| 567 | + && currencyComp.size() >= real.size(); |
| 568 | + } |
| 569 | + |
| 570 | + private static int compareIntegerRange(Type a, Type b) { |
| 571 | + if (!a.isInteger() || !b.isInteger()) { |
| 572 | + return 0; |
651 | 573 | }
|
| 574 | + |
| 575 | + IntegerType intA = (IntegerType) a; |
| 576 | + IntegerType intB = (IntegerType) b; |
| 577 | + |
| 578 | + if (valueRangesAreAmbiguous(intA, intB)) { |
| 579 | + return 0; |
| 580 | + } |
| 581 | + |
| 582 | + return ComparisonChain.start() |
| 583 | + .compare(intA, intB, Comparator.comparing(IntegerType::max)) |
| 584 | + .compare(intB, intA, Comparator.comparing(IntegerType::min)) |
| 585 | + .result(); |
| 586 | + } |
| 587 | + |
| 588 | + private static boolean valueRangesAreAmbiguous(IntegerType a, IntegerType b) { |
| 589 | + return a.isSigned() == b.isSigned() |
| 590 | + && !(a.min().compareTo(b.min()) <= 0 && a.max().compareTo(b.max()) >= 0) |
| 591 | + && !(b.min().compareTo(a.min()) <= 0 && b.max().compareTo(a.max()) >= 0); |
| 592 | + } |
| 593 | + |
| 594 | + private static int compareStringType(Type a, Type b) { |
| 595 | + if (!a.isString() || !b.isString()) { |
| 596 | + return 0; |
| 597 | + } |
| 598 | + |
| 599 | + return Comparator.<Type>comparingInt( |
| 600 | + type -> { |
| 601 | + type = TypeUtils.findBaseType(type); |
| 602 | + if (type.is(IntrinsicType.WIDESTRING)) { |
| 603 | + return 1; |
| 604 | + } else if (type.is(IntrinsicType.UNICODESTRING)) { |
| 605 | + return 2; |
| 606 | + } else if (type.isAnsiString()) { |
| 607 | + return 3; |
| 608 | + } else { |
| 609 | + return 4; |
| 610 | + } |
| 611 | + }) |
| 612 | + .reversed() |
| 613 | + .compare(a, b); |
652 | 614 | }
|
653 | 615 | }
|
0 commit comments