@@ -641,30 +641,47 @@ using GenerateFn = llvm::function_ref<void(
641
641
CompilerInvocation &, SmallVectorImpl<const char *> &,
642
642
CompilerInvocation::StringAllocator)>;
643
643
644
- // May perform round-trip of command line arguments. By default, the round-trip
645
- // is enabled in assert builds. This can be overwritten at run-time via the
646
- // "-round-trip-args" and "-no-round-trip-args" command line flags.
647
- // During round-trip, the command line arguments are parsed into a dummy
648
- // instance of CompilerInvocation which is used to generate the command line
649
- // arguments again. The real CompilerInvocation instance is then created by
650
- // parsing the generated arguments, not the original ones.
644
+ // / May perform round-trip of command line arguments. By default, the round-trip
645
+ // / is enabled in assert builds. This can be overwritten at run-time via the
646
+ // / "-round-trip-args" and "-no-round-trip-args" command line flags, or via the
647
+ // / ForceRoundTrip parameter.
648
+ // /
649
+ // / During round-trip, the command line arguments are parsed into a dummy
650
+ // / CompilerInvocation, which is used to generate the command line arguments
651
+ // / again. The real CompilerInvocation is then created by parsing the generated
652
+ // / arguments, not the original ones. This (in combination with tests covering
653
+ // / argument behavior) ensures the generated command line is complete (doesn't
654
+ // / drop/mangle any arguments).
655
+ // /
656
+ // / Finally, we check the command line that was used to create the real
657
+ // / CompilerInvocation instance. By default, we compare it to the command line
658
+ // / the real CompilerInvocation generates. This checks whether the generator is
659
+ // / deterministic. If \p CheckAgainstOriginalInvocation is enabled, we instead
660
+ // / compare it to the original command line to verify the original command-line
661
+ // / was canonical and can round-trip exactly.
651
662
static bool RoundTrip (ParseFn Parse, GenerateFn Generate,
652
663
CompilerInvocation &RealInvocation,
653
664
CompilerInvocation &DummyInvocation,
654
665
ArrayRef<const char *> CommandLineArgs,
655
- DiagnosticsEngine &Diags, const char *Argv0) {
666
+ DiagnosticsEngine &Diags, const char *Argv0,
667
+ bool CheckAgainstOriginalInvocation = false ,
668
+ bool ForceRoundTrip = false ) {
656
669
#ifndef NDEBUG
657
670
bool DoRoundTripDefault = true ;
658
671
#else
659
672
bool DoRoundTripDefault = false ;
660
673
#endif
661
674
662
675
bool DoRoundTrip = DoRoundTripDefault;
663
- for (const auto *Arg : CommandLineArgs) {
664
- if (Arg == StringRef (" -round-trip-args" ))
665
- DoRoundTrip = true ;
666
- if (Arg == StringRef (" -no-round-trip-args" ))
667
- DoRoundTrip = false ;
676
+ if (ForceRoundTrip) {
677
+ DoRoundTrip = true ;
678
+ } else {
679
+ for (const auto *Arg : CommandLineArgs) {
680
+ if (Arg == StringRef (" -round-trip-args" ))
681
+ DoRoundTrip = true ;
682
+ if (Arg == StringRef (" -no-round-trip-args" ))
683
+ DoRoundTrip = false ;
684
+ }
668
685
}
669
686
670
687
// If round-trip was not requested, simply run the parser with the real
@@ -719,30 +736,34 @@ static bool RoundTrip(ParseFn Parse, GenerateFn Generate,
719
736
// Generate arguments from the dummy invocation. If Generate is the
720
737
// inverse of Parse, the newly generated arguments must have the same
721
738
// semantics as the original.
722
- SmallVector<const char *> GeneratedArgs1 ;
723
- Generate (DummyInvocation, GeneratedArgs1 , SA);
739
+ SmallVector<const char *> GeneratedArgs ;
740
+ Generate (DummyInvocation, GeneratedArgs , SA);
724
741
725
742
// Run the second parse, now on the generated arguments, and with the real
726
743
// invocation and diagnostics. The result is what we will end up using for the
727
744
// rest of compilation, so if Generate is not inverse of Parse, something down
728
745
// the line will break.
729
- bool Success2 = Parse (RealInvocation, GeneratedArgs1 , Diags, Argv0);
746
+ bool Success2 = Parse (RealInvocation, GeneratedArgs , Diags, Argv0);
730
747
731
748
// The first parse on original arguments succeeded, but second parse of
732
749
// generated arguments failed. Something must be wrong with the generator.
733
750
if (!Success2) {
734
751
Diags.Report (diag::err_cc1_round_trip_ok_then_fail);
735
752
Diags.Report (diag::note_cc1_round_trip_generated)
736
- << 1 << SerializeArgs (GeneratedArgs1 );
753
+ << 1 << SerializeArgs (GeneratedArgs );
737
754
return false ;
738
755
}
739
756
740
- // Generate arguments again, this time from the options we will end up using
741
- // for the rest of the compilation.
742
- SmallVector<const char *> GeneratedArgs2;
743
- Generate (RealInvocation, GeneratedArgs2, SA);
757
+ SmallVector<const char *> ComparisonArgs;
758
+ if (CheckAgainstOriginalInvocation)
759
+ // Compare against original arguments.
760
+ ComparisonArgs.assign (CommandLineArgs.begin (), CommandLineArgs.end ());
761
+ else
762
+ // Generate arguments again, this time from the options we will end up using
763
+ // for the rest of the compilation.
764
+ Generate (RealInvocation, ComparisonArgs, SA);
744
765
745
- // Compares two lists of generated arguments.
766
+ // Compares two lists of arguments.
746
767
auto Equal = [](const ArrayRef<const char *> A,
747
768
const ArrayRef<const char *> B) {
748
769
return std::equal (A.begin (), A.end (), B.begin (), B.end (),
@@ -754,23 +775,41 @@ static bool RoundTrip(ParseFn Parse, GenerateFn Generate,
754
775
// If we generated different arguments from what we assume are two
755
776
// semantically equivalent CompilerInvocations, the Generate function may
756
777
// be non-deterministic.
757
- if (!Equal (GeneratedArgs1, GeneratedArgs2 )) {
778
+ if (!Equal (GeneratedArgs, ComparisonArgs )) {
758
779
Diags.Report (diag::err_cc1_round_trip_mismatch);
759
780
Diags.Report (diag::note_cc1_round_trip_generated)
760
- << 1 << SerializeArgs (GeneratedArgs1 );
781
+ << 1 << SerializeArgs (GeneratedArgs );
761
782
Diags.Report (diag::note_cc1_round_trip_generated)
762
- << 2 << SerializeArgs (GeneratedArgs2 );
783
+ << 2 << SerializeArgs (ComparisonArgs );
763
784
return false ;
764
785
}
765
786
766
787
Diags.Report (diag::remark_cc1_round_trip_generated)
767
- << 1 << SerializeArgs (GeneratedArgs1 );
788
+ << 1 << SerializeArgs (GeneratedArgs );
768
789
Diags.Report (diag::remark_cc1_round_trip_generated)
769
- << 2 << SerializeArgs (GeneratedArgs2 );
790
+ << 2 << SerializeArgs (ComparisonArgs );
770
791
771
792
return Success2;
772
793
}
773
794
795
+ bool CompilerInvocation::checkCC1RoundTrip (ArrayRef<const char *> Args,
796
+ DiagnosticsEngine &Diags,
797
+ const char *Argv0) {
798
+ CompilerInvocation DummyInvocation1, DummyInvocation2;
799
+ return RoundTrip (
800
+ [](CompilerInvocation &Invocation, ArrayRef<const char *> CommandLineArgs,
801
+ DiagnosticsEngine &Diags, const char *Argv0) {
802
+ return CreateFromArgsImpl (Invocation, CommandLineArgs, Diags, Argv0);
803
+ },
804
+ [](CompilerInvocation &Invocation, SmallVectorImpl<const char *> &Args,
805
+ StringAllocator SA) {
806
+ Args.push_back (" -cc1" );
807
+ Invocation.generateCC1CommandLine (Args, SA);
808
+ },
809
+ DummyInvocation1, DummyInvocation2, Args, Diags, Argv0,
810
+ /* CheckAgainstOriginalInvocation=*/ true , /* ForceRoundTrip=*/ true );
811
+ }
812
+
774
813
static void addDiagnosticArgs (ArgList &Args, OptSpecifier Group,
775
814
OptSpecifier GroupWithValue,
776
815
std::vector<std::string> &Diagnostics) {
0 commit comments