@@ -164,6 +164,85 @@ SourceSelectionArgument::fromString(StringRef Value) {
164164 return nullptr ;
165165}
166166
167+ // / Stores the parsed `-location` argument.
168+ class AbstractSourceLocationArgument {
169+ public:
170+ virtual ~AbstractSourceLocationArgument () {}
171+
172+ // / Parse the `-location` argument.
173+ // /
174+ // / \returns A valid argument when the parse succedeed, null otherwise.
175+ static std::unique_ptr<AbstractSourceLocationArgument>
176+ fromString (StringRef Value);
177+
178+ // / Prints any additional state associated with the location argument to
179+ // / the given output stream.
180+ virtual void print (raw_ostream &OS) {}
181+
182+ // / Returns a replacement refactoring result consumer (if any) that should
183+ // / consume the results of a refactoring operation.
184+ // /
185+ // / The replacement refactoring result consumer is used by \c
186+ // / TestSourceLocationArgument to inject a test-specific result handling
187+ // / logic into the refactoring operation. The test-specific consumer
188+ // / ensures that the individual results in a particular test group are
189+ // / identical.
190+ virtual std::unique_ptr<ClangRefactorToolConsumerInterface>
191+ createCustomConsumer () {
192+ return nullptr ;
193+ }
194+
195+ // / Runs the given refactoring function for each specified location.
196+ // /
197+ // / \returns true if an error occurred, false otherwise.
198+ virtual bool
199+ forAllLocations (const SourceManager &SM,
200+ llvm::function_ref<void (SourceLocation L)> Callback) = 0;
201+ };
202+
203+ // / Stores the parsed -location=filename:line:column option.
204+ class SourceLocationArgument final : public AbstractSourceLocationArgument {
205+ public:
206+ SourceLocationArgument (ParsedSourceLocation Location)
207+ : Location(std::move(Location)) {}
208+
209+ bool forAllLocations (
210+ const SourceManager &SM,
211+ llvm::function_ref<void (SourceLocation L)> Callback) override {
212+ auto FE = SM.getFileManager ().getFile (Location.FileName );
213+ FileID FID = FE ? SM.translateFile (*FE) : FileID ();
214+ if (!FE || FID.isInvalid ()) {
215+ llvm::errs () << " error: -location=" << Location.FileName
216+ << " :... : given file is not in the target TU\n " ;
217+ return true ;
218+ }
219+
220+ SourceLocation Loc = SM.getMacroArgExpandedLocation (
221+ SM.translateLineCol (FID, Location.Line , Location.Column ));
222+ if (Loc.isInvalid ()) {
223+ llvm::errs () << " error: -location=" << Location.FileName << ' :'
224+ << Location.Line << ' :' << Location.Column
225+ << " : invalid source location\n " ;
226+ return true ;
227+ }
228+ Callback (Loc);
229+ return false ;
230+ }
231+
232+ private:
233+ ParsedSourceLocation Location;
234+ };
235+
236+ std::unique_ptr<AbstractSourceLocationArgument>
237+ AbstractSourceLocationArgument::fromString (StringRef Value) {
238+ ParsedSourceLocation Location = ParsedSourceLocation::FromString (Value);
239+ if (Location.FileName != " " )
240+ return std::make_unique<SourceLocationArgument>(std::move (Location));
241+ llvm::errs () << " error: '-location' option must be specified using "
242+ " <file>:<line>:<column>\n " ;
243+ return nullptr ;
244+ }
245+
167246// / A container that stores the command-line options used by a single
168247// / refactoring option.
169248class RefactoringActionCommandLineOptions {
@@ -272,6 +351,17 @@ class RefactoringActionSubcommand : public cl::SubCommand {
272351 break ;
273352 }
274353 }
354+ // Check if the location option is supported.
355+ for (const auto &Rule : this ->ActionRules ) {
356+ if (Rule->hasLocationRequirement ()) {
357+ Location = std::make_unique<cl::opt<std::string>>(
358+ " location" ,
359+ cl::desc (" Location where refactoring should "
360+ " be initiated( <file>:<line>:<column>)" ),
361+ cl::cat (Category), cl::sub (*this ));
362+ break ;
363+ }
364+ }
275365 // Create the refactoring options.
276366 for (const auto &Rule : this ->ActionRules ) {
277367 CommandLineRefactoringOptionCreator OptionCreator (Category, *this ,
@@ -296,11 +386,28 @@ class RefactoringActionSubcommand : public cl::SubCommand {
296386 return false ;
297387 }
298388
389+ // / Parses the "-location" command-line argument.
390+ // /
391+ // / \returns true on error, false otherwise.
392+ bool parseLocationArgument () {
393+ if (Location) {
394+ ParsedLocation = AbstractSourceLocationArgument::fromString (*Location);
395+ if (!ParsedLocation)
396+ return true ;
397+ }
398+ return false ;
399+ }
400+
299401 SourceSelectionArgument *getSelection () const {
300402 assert (Selection && " selection not supported!" );
301403 return ParsedSelection.get ();
302404 }
303405
406+ AbstractSourceLocationArgument *getLocation () const {
407+ assert (Location && " location not supported!" );
408+ return ParsedLocation.get ();
409+ }
410+
304411 const RefactoringActionCommandLineOptions &getOptions () const {
305412 return Options;
306413 }
@@ -309,7 +416,9 @@ class RefactoringActionSubcommand : public cl::SubCommand {
309416 std::unique_ptr<RefactoringAction> Action;
310417 RefactoringActionRules ActionRules;
311418 std::unique_ptr<cl::opt<std::string>> Selection;
419+ std::unique_ptr<cl::opt<std::string>> Location;
312420 std::unique_ptr<SourceSelectionArgument> ParsedSelection;
421+ std::unique_ptr<AbstractSourceLocationArgument> ParsedLocation;
313422 RefactoringActionCommandLineOptions Options;
314423};
315424
@@ -399,6 +508,7 @@ class ClangRefactorTool {
399508 // consumer.
400509 std::unique_ptr<ClangRefactorToolConsumerInterface> TestConsumer;
401510 bool HasSelection = MatchingRule->hasSelectionRequirement ();
511+ bool HasLocation = MatchingRule->hasLocationRequirement ();
402512 if (HasSelection)
403513 TestConsumer = SelectedSubcommand->getSelection ()->createCustomConsumer ();
404514 ClangRefactorToolConsumerInterface *ActiveConsumer =
@@ -424,6 +534,19 @@ class ClangRefactorTool {
424534 ActiveConsumer->endTU ();
425535 return ;
426536 }
537+ if (HasLocation) {
538+ assert (SelectedSubcommand->getLocation () && " Missing location argument?" );
539+ if (opts::Verbose)
540+ SelectedSubcommand->getLocation ()->print (llvm::outs ());
541+ if (SelectedSubcommand->getLocation ()->forAllLocations (
542+ Context.getSources (), [&](SourceLocation L) {
543+ Context.setLocation (L);
544+ InvokeRule (*ActiveConsumer);
545+ }))
546+ HasFailed = true ;
547+ ActiveConsumer->endTU ();
548+ return ;
549+ }
427550 InvokeRule (*ActiveConsumer);
428551 ActiveConsumer->endTU ();
429552 }
@@ -528,6 +651,12 @@ class ClangRefactorTool {
528651 R.getEnd ().print (llvm::outs (), Context.getSources ());
529652 llvm::outs () << " \n " ;
530653 }
654+ if (Context.getLocation ().isValid ()) {
655+ SourceLocation L = Context.getLocation ();
656+ llvm::outs () << " -location=" ;
657+ L.print (llvm::outs (), Context.getSources ());
658+ llvm::outs () << " \n " ;
659+ }
531660 }
532661
533662 llvm::Expected<RefactoringActionRule *>
@@ -539,16 +668,24 @@ class ClangRefactorTool {
539668 CommandLineRefactoringOptionVisitor Visitor (Subcommand.getOptions ());
540669 Rule->visitRefactoringOptions (Visitor);
541670 if (Visitor.getMissingRequiredOptions ().empty ()) {
542- if (!Rule->hasSelectionRequirement ()) {
543- MatchingRules.push_back (Rule.get ());
544- } else {
671+ bool HasMissingOptions = false ;
672+ if (Rule->hasSelectionRequirement ()) {
545673 Subcommand.parseSelectionArgument ();
546- if (Subcommand.getSelection ()) {
547- MatchingRules.push_back (Rule.get ());
548- } else {
674+ if (!Subcommand.getSelection ()) {
549675 MissingOptions.insert (" selection" );
676+ HasMissingOptions = true ;
550677 }
551678 }
679+ if (Rule->hasLocationRequirement ()) {
680+ Subcommand.parseLocationArgument ();
681+ if (!Subcommand.getLocation ()) {
682+ MissingOptions.insert (" location" );
683+ HasMissingOptions = true ;
684+ }
685+ }
686+ if (!HasMissingOptions) {
687+ MatchingRules.push_back (Rule.get ());
688+ }
552689 }
553690 for (const RefactoringOption *Opt : Visitor.getMissingRequiredOptions ())
554691 MissingOptions.insert (Opt->getName ());
0 commit comments