@@ -641,30 +641,47 @@ using GenerateFn = llvm::function_ref<void(
641641 CompilerInvocation &, SmallVectorImpl<const char *> &,
642642 CompilerInvocation::StringAllocator)>;
643643
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.
651662static bool RoundTrip (ParseFn Parse, GenerateFn Generate,
652663 CompilerInvocation &RealInvocation,
653664 CompilerInvocation &DummyInvocation,
654665 ArrayRef<const char *> CommandLineArgs,
655- DiagnosticsEngine &Diags, const char *Argv0) {
666+ DiagnosticsEngine &Diags, const char *Argv0,
667+ bool CheckAgainstOriginalInvocation = false ,
668+ bool ForceRoundTrip = false ) {
656669#ifndef NDEBUG
657670 bool DoRoundTripDefault = true ;
658671#else
659672 bool DoRoundTripDefault = false ;
660673#endif
661674
662675 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+ }
668685 }
669686
670687 // If round-trip was not requested, simply run the parser with the real
@@ -719,30 +736,34 @@ static bool RoundTrip(ParseFn Parse, GenerateFn Generate,
719736 // Generate arguments from the dummy invocation. If Generate is the
720737 // inverse of Parse, the newly generated arguments must have the same
721738 // semantics as the original.
722- SmallVector<const char *> GeneratedArgs1 ;
723- Generate (DummyInvocation, GeneratedArgs1 , SA);
739+ SmallVector<const char *> GeneratedArgs ;
740+ Generate (DummyInvocation, GeneratedArgs , SA);
724741
725742 // Run the second parse, now on the generated arguments, and with the real
726743 // invocation and diagnostics. The result is what we will end up using for the
727744 // rest of compilation, so if Generate is not inverse of Parse, something down
728745 // the line will break.
729- bool Success2 = Parse (RealInvocation, GeneratedArgs1 , Diags, Argv0);
746+ bool Success2 = Parse (RealInvocation, GeneratedArgs , Diags, Argv0);
730747
731748 // The first parse on original arguments succeeded, but second parse of
732749 // generated arguments failed. Something must be wrong with the generator.
733750 if (!Success2) {
734751 Diags.Report (diag::err_cc1_round_trip_ok_then_fail);
735752 Diags.Report (diag::note_cc1_round_trip_generated)
736- << 1 << SerializeArgs (GeneratedArgs1 );
753+ << 1 << SerializeArgs (GeneratedArgs );
737754 return false ;
738755 }
739756
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);
744765
745- // Compares two lists of generated arguments.
766+ // Compares two lists of arguments.
746767 auto Equal = [](const ArrayRef<const char *> A,
747768 const ArrayRef<const char *> B) {
748769 return std::equal (A.begin (), A.end (), B.begin (), B.end (),
@@ -754,23 +775,41 @@ static bool RoundTrip(ParseFn Parse, GenerateFn Generate,
754775 // If we generated different arguments from what we assume are two
755776 // semantically equivalent CompilerInvocations, the Generate function may
756777 // be non-deterministic.
757- if (!Equal (GeneratedArgs1, GeneratedArgs2 )) {
778+ if (!Equal (GeneratedArgs, ComparisonArgs )) {
758779 Diags.Report (diag::err_cc1_round_trip_mismatch);
759780 Diags.Report (diag::note_cc1_round_trip_generated)
760- << 1 << SerializeArgs (GeneratedArgs1 );
781+ << 1 << SerializeArgs (GeneratedArgs );
761782 Diags.Report (diag::note_cc1_round_trip_generated)
762- << 2 << SerializeArgs (GeneratedArgs2 );
783+ << 2 << SerializeArgs (ComparisonArgs );
763784 return false ;
764785 }
765786
766787 Diags.Report (diag::remark_cc1_round_trip_generated)
767- << 1 << SerializeArgs (GeneratedArgs1 );
788+ << 1 << SerializeArgs (GeneratedArgs );
768789 Diags.Report (diag::remark_cc1_round_trip_generated)
769- << 2 << SerializeArgs (GeneratedArgs2 );
790+ << 2 << SerializeArgs (ComparisonArgs );
770791
771792 return Success2;
772793}
773794
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+
774813static void addDiagnosticArgs (ArgList &Args, OptSpecifier Group,
775814 OptSpecifier GroupWithValue,
776815 std::vector<std::string> &Diagnostics) {
0 commit comments