coan  6.0.1
A C/C++ Configuration Analyzer
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
symbol.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2007-2013 Mike Kinghan, imk@burroingroingjoing.com *
3  * All rights reserved. *
4  * *
5  * Contributed originally by Mike Kinghan, imk@burroingroingjoing.com *
6  * *
7  * Redistribution and use in source and binary forms, with or without *
8  * modification, are permitted provided that the following conditions *
9  * are met: *
10  * *
11  * Redistributions of source code must retain the above copyright *
12  * notice, this list of conditions and the following disclaimer. *
13  * *
14  * Redistributions in binary form must reproduce the above copyright *
15  * notice, this list of conditions and the following disclaimer in the *
16  * documentation and/or other materials provided with the distribution. *
17  * *
18  * Neither the name of Mike Kinghan nor the names of its contributors *
19  * may be used to endorse or promote products derived from this software *
20  * without specific prior written permission. *
21  * *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, *
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, *
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS *
29  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED *
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,*
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF *
32  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
33  * DAMAGE. *
34  * *
35  **************************************************************************/
36 
37 #include "symbol.h"
38 #include "line_despatch.h"
39 #include "diagnostic.h"
40 #include "reference.h"
41 #include "canonical.h"
42 #include "contradiction.h"
43 #include "if_control.h"
44 #include <algorithm>
45 #include <cstring>
46 #include <cassert>
47 
52 
54 using namespace std;
55 
57 symbol::_null_("",symbol(provenance::unconfigured));
58 
59 map<string,symbol>
61 
63 
66 
67 int symbol::snapshot_max() const
68 {
69  int max = _snapshot;
70  auto first = _contributors.begin();
71  auto last = _contributors.end();
72  for ( ;first != last; ++first) {
73  int submax = (*first)->snapshot_max();
74  if (max < submax) {
75  max = submax;
76  }
77  }
78  return max;
79 }
80 
81 void symbol::set_definition(string const & defn)
82 {
83  _defn.reset(new string(defn));
84 }
85 
86 void symbol::define(string const & defn, formal_parameter_list const & params)
87 {
89  make_dirty(pseudo_snapshot::define_in_progress);
90  unsubscribe();
91  set_definition(defn);
92  set_parameters(params);
93  subscribe();
94  _line = line_despatch::cur_line().num();
95 }
96 
97 void symbol::undef()
98 {
100  make_dirty(pseudo_snapshot::undef_in_progress);
101  unsubscribe();
102  _defn.reset();
103  _params = formal_parameter_list();
104  _format.reset();
105  _line = line_despatch::cur_line().num();
106 }
107 
108 bool symbol::selected(string const & name)
109 {
110  set<string>::const_iterator lb =
111  _selected_symbols_set_.lower_bound(name);
112  if (lb != _selected_symbols_set_.end() &&
113  (name == *lb || wildcard_match(*lb,name))) {
114  return true;
115  }
116  return lb != _selected_symbols_set_.begin() && wildcard_match(*--lb,name);
117 }
118 
119 bool symbol::deselected(string const & id)
120 {
121  return options::selected_symbols() && !selected(id);
122 }
123 
124 bool symbol::wildcard_match(string const & wildcard, string const & name)
125 {
126 #if CXX11_HAVE_STRING_BACK
127  if (wildcard.back() != '*') {
128 #else
129  if (*(--wildcard.end()) != '*') {
130 #endif
131  return false;
132  }
133  size_t min_len = wildcard.length() - 1;
134  if (name.length() < min_len) {
135  return false;
136  }
137  return memcmp(name.data(),wildcard.data(),min_len) == 0;
138 }
139 
140 void symbol::set_selection(char const *optarg)
141 {
142  string arg(optarg);
143  size_t follower = 0;
144  size_t leader = arg.find(',');
145  for ( ;leader != string::npos;
146  follower = leader + 1,leader = arg.find(',',follower)) {
147  string pattern = arg.substr(follower,leader - follower);
148  if (pattern.length()) {
149  add_pattern(pattern);
150  }
151  }
152  if (follower < arg.length()) {
153  add_pattern(arg.substr(follower));
154  }
155 }
156 
157 
159  auto first = _contributors.begin();
160  auto const last = _contributors.end();
161  for ( ;first != last; ++first) {
162  (*first)->report_premiere();
163 
164  }
165  if (origin() == provenance::global) {
166  reference ref(_loc);
167  if (!ref.reported()) {
168  ref.report();
169  }
170  }
171 }
172 
173 size_t symbol::count(provenance source)
174 {
175  auto i = _sym_tab_.begin();
176  size_t nsyms = 0;
177  for ( ; i != _sym_tab_.end(); ++i) {
178  nsyms += i->second._provenance == source;
179  }
180  return nsyms;
181 }
182 
184 {
185  static bool done = false;
186  if (done) {
187  return;
188  }
189  done = true;
191  auto i = _sym_tab_.begin();
192  for ( i = _sym_tab_.begin(); i != _sym_tab_.end(); ++i) {
193  if (i->second.origin() == provenance::global) {
194  i->second.report_premiere();
195  }
196  }
197 }
198 
199 void symbol::subscribe_to(locator other)
200 {
201  if (which_parameter(other.id()) != string::npos) {
202  return;
203  }
204  if (_loc == other) {
205  make_self_referential();
206  return;
207  }
208  if (other->subscribes_to(_loc)) {
209  make_self_referential();
210  other->make_self_referential();
211  return;
212  }
213  if (find(_contributors.begin(),_contributors.end(),other) ==
214  _contributors.end()) {
215  _contributors.push_back(other);
216  other->_subscribers.push_back(_loc);
217  }
218  for (auto i = other->_contributors.begin();
219  i != other->_contributors.end(); ++i) {
220  subscribe_to(*i);
221  }
222 }
223 
224 bool symbol::subscribes_to(symbol::locator other) const {
225  return find(_contributors.begin(),_contributors.end(),other) !=
226  _contributors.end();
227 }
228 
229 void symbol::subscribe()
230 {
231  if (!_defn || _defn->empty()) {
232  return;
233  }
234  assert(_contributors.empty());
235  chewer<string> chew(chew_mode::plaintext,*_defn);
236  string id;
237  size_t off = 0;
238  while(((id = identifier::find_any_in(chew,off)),!id.empty())) {
239  locator loc(id);
240  subscribe_to(loc);
241  argument_list args(chew);
242  if (args && _loc != loc && !loc->configured() && !loc->invoked()) {
243  loc->set_parameters(args.size());
244  }
245  }
246 }
247 
248 void symbol::unsubscribe() {
249  auto i = _contributors.begin();
250  for ( ;i != _contributors.end(); ++i) {
251  auto & contributes_to = (*i)->_subscribers;
252  auto j = find(contributes_to.begin(),contributes_to.end(),_loc);
253  if (j != contributes_to.end()) {
254  contributes_to.erase(j);
255  }
256  }
257  _contributors.clear();
258 }
259 
261 {
262  if (count() < size_t(_last_global_snapshot_) + 1) {
263  return;
264  }
265  // Skip the null symbol
266  auto i = ++_sym_tab_.begin();
267  // Unsubscribe all symbols
268  for ( ;i != _sym_tab_.end(); ++i) {
269  i->second.unsubscribe();
270  }
271 
272  // Delete all transients
273  for (i = ++_sym_tab_.begin(); i != _sym_tab_.end();) {
274  if (i->second.origin() == provenance::transient) {
275  i = _sym_tab_.erase(i);
276  } else {
277  ++i;
278  }
279  }
280 
281  // Prep remaining symbols
282  for (i = ++_sym_tab_.begin(); i != _sym_tab_.end(); ++i) {
285  }
286  if (i->second.origin() == provenance::global) {
287  i->second.subscribe();
288  } else {
289  i->second.clear_parameters();
290  i->second.set_invoked(false);
291  }
292  }
293  _current_snapshot_ = count();
295  report_global_config();
296  }
297 }
298 
299 void symbol::report() const
300 {
301  if (!line_despatch::cur_line().reportable() ||
303  return;
304  }
305  reference ref(_loc);
306  if (!clean() && !_contributors.empty()) {
307  assert(_defn);
308  for (auto i = _contributors.begin(); i != _contributors.end(); ++i) {
309  size_t off = 0;
310  chewer<string> chew(chew_mode::plaintext,*_defn);
311  string const & id = (*i)->id();
312  vector<argument_list> args_seen;
313  while ((off = identifier::find_first_in(id,chew)) != string::npos) {
314  argument_list args(chew);
315  if (find(args_seen.begin(),args_seen.end(),args)
316  == args_seen.end()) {
317  args_seen.push_back(args);
318  reference r(*i,args,&ref);
319  r.report();
320  }
321  }
322  }
323  }
324  ref.report();
325 }
326 
327 void symbol::make_dirty(pseudo_snapshot n) {
328  char const * action = nullptr;
329  switch(n) {
330  case pseudo_snapshot::define_in_progress:
331  action = "Defining";
332  break;
333  case pseudo_snapshot::undef_in_progress:
334  action = "Undefining";
335  break;
336  case pseudo_snapshot::infinite:
337  break;
338  default:
339  assert(false);
340  }
341  for (auto i = _subscribers.begin(); i != _subscribers.end(); ++i) {
342  if (!(*i)->dirty()) {
343  (*i)->make_dirty(n);
344  }
345  if (action) {
347  << action << " \"" << id()
348  << "\" retrospectively affects the meaning of \""
349  << (*i)->signature() << '\"' << emit();
350  }
351  }
352  _snapshot = int(n);
353 }
354 
356 {
357  string definition;
358  size_t off = size_t(chew);
359  formal_parameter_list params(chew);
360  if (!params.well_formed()) {
362  "Malformed macro parameter list for symbol \""
363  << id() << "\"" << emit();
364  return;
365  }
366  if (*chew == '=') {
367  ++chew;
368  } else if (chew) {
370  << "Garbage in \"-D" << id()
371  << chew.buf().substr(off) << '\"' << emit();
372  return;
373  }
374  definition = canonical<string>(chew);
375  if (configured()) {
376  /* -D option defining prior global */
377  if (_defn) { /* symbol is already defined */
378  if (definition != *_defn ||
379  params.size() != parameters().size()) {
380  /* Definitions differ */
381  error_invalid_args() << "\"-D" << id() << params.str()
382  << '=' << definition
383  << "\" contradicts prior \"-D" << id()
384  << parameters().str()
385  << '=' << *_defn << '\"' << emit();
386  return;
387  } else { /* Definitions the same */
389  << "Duplicated \"-D" << id() << parameters().str()
390  << '=' << definition << "\" ignored" << emit();
391  return;
392  }
393  } else {
394  /* -D contradicts prior -U option */
395  error_invalid_args() << "\"-D" << id() << params.str()
396  << '=' << definition << "\" contradicts prior \"-U"
397  << id() << '\"' << emit();
398  return;
399  }
400  }
401  set_pseudo_snapshot(pseudo_snapshot::define_in_progress);
402  set_definition(definition);
403  set_parameters(params);
404  _provenance = provenance::global;
405 }
406 
407 line_type
409  string const & definition)
410 {
411  if (configured()) {
412  bool is_global = _provenance == provenance::global;
413  /* This is a re-configuration of an already configured symbol */
414  if (_defn) { /* symbol is already defined */
415  if (definition != *_defn ||
416  params.size() != parameters().size()) {
417  if (!is_global) {
418  /* Differing #define contradicts prior #define */
419  warning_differing_redef() << "Differently redefining \""
420  << id() << params.str()
421  << '=' << definition << "\", after \""
422  << id() << parameters().str()
423  << '=' << *_defn << "\" at line "
424  << _line << emit();
425  } else {
426  /* Differing #define contradicts -D option */
430  return LT_DIRECTIVE_DROP;
431  }
432  }
433  /* Definitions the same */
434  else if (!is_global) {
435  /* #define duplicating #define */
436  return LT_DIRECTIVE_KEEP;
437  } else { /* #define duplicating -D option */
440  }
441  return LT_DIRECTIVE_DROP;
442  }
443  }
444  /* symbol is already undefined. */
445  else if (is_global) {
446  /* #define contradicts -U option */
448  return LT_DIRECTIVE_DROP;
449  }
450  //* Else #define countervails #undef */
451  }
453  if (!configured()) {
455  << "\"-D" << id() << params.str()
456  << '=' << definition
457  << "\" has been assumed for the current file" << emit();
458  }
459  define(definition,params);
460  _provenance = provenance::transient;
461  } else if (!options::no_transients()) {
462  /* If we are here the symbol must be unconfigured.
463  If it were configured global then we have already returned.
464  Hence it could only be configured transient, which is not allowed.
465  */
466  set_parameters(params);
467  _provenance = provenance::unconfigured;
468  }
469  return LT_DIRECTIVE_KEEP;
470 }
471 
473 {
474  if (chew) {
475  error_garbage_arg() << "Garbage in \"#undef " << id() << '\"' << emit();
476  return;
477  }
478  if (configured()) {
479  if (_defn) { /* symbol is already defined */
480  error_invalid_args() << "\"-U" << id() << parameters().str()
481  << "\" contradicts prior \"-D" << id() << '='
482  << *_defn << '\"' << emit();
483  return;
484  }
485  /* symbol is already undefined. */
486  else {
487  warning_duplicate_args() << "Duplicated \"-U"
488  << id() << "\" ignored" << emit();
489  return;
490  }
491  }
492  set_pseudo_snapshot(pseudo_snapshot::undef_in_progress);
493  undef();
494  _provenance = provenance::global;
495 }
496 
497 line_type
499 {
500  if (configured()) {
501  bool is_global = _provenance == provenance::global;
502  if (_defn) { /* symbol is already defined */
503  if (!is_global) {
504  /* #undef contradicting prior #define */
506  << "undefining " << id() << ", after defining "
507  << id() << parameters().str()
508  << '=' << *_defn << " at line "
509  << _line << emit();
510  } else { /* #undef contradicting -D option */
514  }
515  return LT_DIRECTIVE_DROP;
516  }
517  }
518  /* symbol is already undefined. */
519  else if (is_global) {
520  /* #undef echoes -U */
521  return LT_DIRECTIVE_DROP;
522  } else {
523  /* #undef echoes #undef */
524  return LT_DIRECTIVE_KEEP;
525  }
526  }
528  if (!configured()) {
529  warning_transient_symbol_added() << "\"-U" << id() <<
530  "\" has been assumed for the current file" << emit();
531  }
532  undef();
533  _provenance = provenance::transient;
534  } else if (!options::no_transients()) {
535  /* If we are here the symbol must be unconfigured.
536  If it were configured global then we have already returned.
537  Hence it could only be configured transient, which is not allowed.
538  */
539  _provenance = provenance::unconfigured;
540  }
541  return LT_DIRECTIVE_KEEP;
542 }
543 
544 template<class CharSeq>
546 {
548  string id;
549  while((id = identifier::find_any_in(chew,off)),!id.empty()) {
550  locator sloc = lookup(id);
551  if (sloc) {
552  return sloc;
553  }
554  }
555  return locator();
556 }
557 
558 template
560 template
562 
564 
565 // EOF
static parsed_line & cur_line()
Get a reference to the current output line.
Definition: line_despatch.h:68
bool well_formed() const
Say whether the parameter_list_base is well-formed.
template struct traits::is_random_access_char_sequence<T> exports a static const boolean member value...
Definition: traits.h:166
error_msg< 8 > error_invalid_args
Report that the commandline options are not a valid combination.
Definition: diagnostic.h:738
static void forget()
Forget about an apparent contradiction.
static void save(cause why, std::string const &symname)
Save diagnostic details of a potential contradiction.
static int _last_global_snapshot_
The last snapshot number consumed by the global configuration.
Definition: symbol.h:622
void set_directive_type(directive_type dtype)
Set the directive type of the line.
Definition: parsed_line.h:132
void undef()
Undefine thesymbol.
std::string str() const
Cast the parameter list to its canonical string representation.
A directive line of no more specific type that is to be kept.
Definition: line_type.h:76
void subscribe_to(locator other)
Record the symbol's definition as referring to another another symbol, and recursively as referring t...
static bool selected_symbols()
Is symbol reporting restricted to a selected set?
Definition: options.h:209
static bool selected(std::string const &id)
Say whether a symbol name matches a selection pattern for reporting.
error_msg< 17 > error_malformed_macro
Report a malformed macro parameter list.
Definition: diagnostic.h:758
The symbols command.
Definition: options.h:76
size_t size() const
Get the number of parameters in the parameter_list_base
std::string find_any_in(chewer< CharSeq > &chew, size_t &off)
Search a terminal portion of a CharSeq for any identifier.
struct symbol::locator encapsulates a symbol table entry.
Definition: symbol.h:79
static locator find_any_in(chewer< CharSeq > &chew, size_t &off)
Search a terminal portion of a CharSeq for any known symbol name.
warning_msg< 5 > warning_differing_redef
Report that an in-source #define defines a symbol differently from a prior one.
Definition: diagnostic.h:623
static command_code get_command()
Get the operative coan command code.
Definition: options.cpp:247
static bool is_unconditional_line()
Is the current line outside any #if scope or in the scope of a satisfied #if?
Definition: if_control.cpp:326
void set_definition(std::string const &defn)
Set the definition of the symbol.
warning_msg< 6 > warning_undefing_defined
Report that an in-source #undef undefines a symbol previously defined by an in-source #define...
Definition: diagnostic.h:628
static bool wildcard_match(std::string const &wildcard, std::string const &name)
Say whether a symbol name matches a *-terminated wildcard prefix.
static symbol_table _sym_tab_
The symbol table.
Definition: symbol.h:626
Class argument_list encapsulates a list of macro arguments, i.e. the arguments to a macro reference...
Definition: argument_list.h:50
line_type digest_transient_define(formal_parameter_list const &params, std::string const &definition)
Analyse and handle an in-source define directive for this symbol.
The commandline, considered as a #define/#undef
size_t find_first_in(std::string const &id, chewer< CharSeq > &chew)
Find the first occurrence of an identifier within a terminal segment a CharSeq
static size_t count()
Get the number of symbols in the symbol table.
Definition: symbol.h:386
static void set_selection(char const *optarg)
void define(std::string const &defn, formal_parameter_list const &params)
Define symbol.
An in-souce #define differently redefines a -D option.
An in-souce #undef contradicts a -D option.
warning_msg< 27 > warning_transient_symbol_added
Definition: diagnostic.h:705
unsigned num() const
Get the greatest source line number spanned by this line.
Definition: parsed_line.h:70
void make_dirty(pseudo_snapshot n=pseudo_snapshot::pristine)
Assign the symbol state a pseudo snapshot number, signifying that it is out of date, and recursively to all its subscribers.
static void report_global_config()
Report the global configuration, according to options.
sequence_type & buf()
Get a [const] reference to the associated sequence_type
Definition: chew.h:413
line_type digest_transient_undef()
Analyse and handle an in-source undef directive for this symbol.
static bool list_once_per_file()
Do we report the listed items just once per input file?
Definition: options.h:121
static bool expand_references()
Do we report the expansions reported symbols?
Definition: options.h:171
A directive line of no more specific type than that is to be dropped.
Definition: line_type.h:74
void report() const
Report a reference to this symbol.
chew_mode::name const name
An exemplar chew_mode::name
Definition: chew.h:229
The tag class is inserted in a diagnostic_base to tell it to emit itself.
Definition: diagnostic.h:77
static int _current_snapshot_
The current sequential snapshot number.
Definition: symbol.h:620
void report_premiere()
Report a symbol as resolved from the global configuration. The method invokes itself recursively on a...
static void insert(cause why, std::string const &symname)
Insert an error diagnostic into the output as a error directive or comment.
An in-souce #define contradicts a -U option.
static std::string const & last_conflicted_symbol_id()
Get the name of the latest #undef-ed symbol.
bool subscribes_to(locator other) const
Say whether the symbol's definition refers to another another symbol, directly or indirectly...
static void per_file_init()
Delete all transient symbols from the symbol table.
static std::set< std::string > _selected_symbols_set_
The set of symbols selected for reporting, if any.
Definition: symbol.h:624
static void flush()
Discharge any pending contradiction.
void subscribe()
Acquire all the symbol's contributors.
warning_msg< 1 > warning_duplicate_args
Report that same argument occurs for multiple --define or --undefine options.
Definition: diagnostic.h:595
info_msg< 3 > info_retrospective_redefinition
Report a #define or #undef that retrospectively affects the meaning of another symbol.
Definition: diagnostic.h:590
symbol_table::value_type table_entry
Type of entry in the symbol table.
Definition: symbol.h:76
`template struct chewer<CharSeq> is a cursor-like type that is associated with a character-sequence t...
Definition: chew.h:248
template class canonical<What> encapsulates the canonical representation of values of type What...
Definition: canonical.h:82
void digest_global_define(chewer< std::string > &chew)
Analyse and handle a -D option for this symbol.
static bool no_transients()
Definition: options.h:201
static table_entry _null_
The table entry of the null symbol.
Definition: symbol.h:628
bool deselected() const
Say whether the symbol is deselected per the --select option.
Definition: symbol.h:264
line_type
Enumeration of types of input lines.
Definition: line_type.h:52
static void erase_symbol(std::string const &id)
Delete all cached references of a given symbol.
struct symbol encapsulates a preprocessor symbol's state
Definition: symbol.h:58
struct formal_parameter_list encapsulates a list of formal macro parameters.
char * optarg
Argument to an option parsed by getopt_long()
Definition: get_options.cpp:49
void unsubscribe()
Forget all the symbol's contributors.
void digest_global_undef(chewer< std::string > &chew)
Analyse and handle a -U option for this symbol.
int snapshot_max() const
Get the maximum sequential snapshot number in the the recursive closure of this symbol its contributo...
error_msg< 12 > error_garbage_arg
Definition: diagnostic.h:748