1 /*
2 * (C) Copyright 2001-2015 Diomidis Spinellis
3 *
4 * This file is part of CScout.
5 *
6 * CScout is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CScout is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CScout. If not, see <http://www.gnu.org/licenses/>.
18 *
19 *
20 * For documentation read the corresponding .h file
21 *
22 */
23
24 #include <map>
25 #include <string>
26 #include <deque>
27 #include <vector>
28 #include <stack>
29 #include <iterator>
30 #include <fstream>
31 #include <iostream>
32 #include <list>
33 #include <set>
34
35 #include "cpp.h"
36 #include "debug.h"
37 #include "error.h"
38 #include "attr.h"
39 #include "metrics.h"
40 #include "fileid.h"
41 #include "tokid.h"
42 #include "token.h"
43 #include "parse.tab.h"
44 #include "ptoken.h"
45 #include "fchar.h"
46 #include "pltoken.h"
47 #include "macro.h"
48 #include "pdtoken.h"
49 #include "ctoken.h"
50 #include "type.h"
51 #include "stab.h"
52 #include "fdep.h"
53 #include "call.h"
54 #include "fcall.h"
55 #include "mcall.h"
56 #include "globobj.h"
57 #include "ctag.h"
58
59
60 int Block::current_block = -1;
61 vectorBlock Block::scope_block;
62 Stab Function::label;
63 Block Block::param_block; // Function parameter declarations
64 bool Block::use_param; // Declare in param_block when true
65
Id(const Token & tok,Type typ,FCall * fc,GlobObj * go)66 Id::Id(const Token& tok, Type typ, FCall *fc, GlobObj *go) :
67 token(tok), type(typ), fcall(fc), glob(go)
68 {
69 }
70
71 // Called when entering a scope
72 void
enter()73 Block::enter()
74 {
75 scope_block.push_back(Block());
76 current_block++;
77 }
78
79 // Called when entering a function block statement
80 void
param_enter()81 Block::param_enter()
82 {
83 if (DP())
84 cout << "On param_enter " << param_block.obj << "\n";
85 scope_block.push_back(param_block);
86 current_block++;
87 use_param = false;
88 }
89
90 void
exit()91 Block::exit()
92 {
93 scope_block.pop_back();
94 current_block--;
95 }
96
97 // Called when exiting a function parameter list
98 void
param_exit()99 Block::param_exit()
100 {
101 // Do not clobber parameter block when exiting function pointer
102 // arguments appearing in old-style argument declarations
103 if (!use_param)
104 param_block = scope_block.back();
105 scope_block.pop_back();
106 current_block--;
107 if (DP())
108 cout << "On param_exit " << param_block.obj << "\n";
109 }
110
111 /*
112 * Define name to be the identifier id
113 */
114 void
define(Stab Block::* table,const Token & tok,const Type & typ,FCall * fc,GlobObj * go)115 Block::define(Stab Block::*table, const Token& tok, const Type& typ, FCall *fc, GlobObj *go)
116 {
117 (scope_block[current_block].*table).define(tok, typ, fc, go);
118 }
119
120 // Called when exiting a function block statement
121 void
param_clear(void)122 Block::param_clear(void)
123 {
124 param_block.obj.clear();
125 param_block.tag.clear();
126 }
127
128 void
clear()129 Block::clear()
130 {
131 param_clear();
132 scope_block.clear();
133 current_block = -1;
134 }
135
136 /*
137 * Define the tok object to be of type typ
138 */
139 void
obj_define(const Token & tok,Type typ)140 obj_define(const Token& tok, Type typ)
141 {
142 tok.set_ec_attribute(is_ordinary);
143 enum e_storage_class sc = typ.get_storage_class();
144 Id const * id;
145
146 if (DP())
147 cout << "Define object [" << tok.get_name() << "]: " << typ << "\n";
148 if (Block::use_param && Block::current_block == Block::cu_block) {
149 // Old-style function definition declarations
150 // No checking
151 if ((id = Block::param_block.obj.lookup(tok.get_name())))
152 Token::unify(id->get_token(), tok);
153 else
154 /*
155 * @error
156 * While processing an old-style (K&R) parameter
157 * declaration, a declared parameter did not match
158 * any of the parameters appearing in the function's
159 * arguments definition
160 */
161 Error::error(E_ERR, "declared parameter does not appear in old-style function parameter list: " + tok.get_name());
162 (Block::param_block.obj).define(tok, typ);
163 return;
164 }
165 if (sc == c_unspecified && typ.is_cfunction())
166 // If the declaration of an identifier for a function has no
167 // storage-class specifier, its linkage is determined exactly
168 // as if it were declared with the storage class specifier
169 // extern 6.2.2-5
170 sc = c_extern;
171 if (sc == c_extern && (id = obj_lookup(tok.get_name()))) {
172 // If the declaration of an identifier contains extern the identifier
173 // has the same linkage as the prior visible declaration of the identifier
174 // 6.2.2-4
175 enum e_storage_class sc2 = id->get_type().get_storage_class();
176 if (sc2 != sc) {
177 typ.set_storage_class(basic(b_abstract, s_none, sc2));
178 sc = sc2;
179 }
180 }
181 if (DP())
182 cout << "Type of " << tok << " is " << typ << (typ.is_typedef() ? " (typedef)\n" : "\n");
183 if (Block::get_scope_level() == Block::cu_block) {
184 // Function *definitions* are added from FCall::set_current_fun
185 // We don't add extern variables
186 if (!typ.is_cfunction() && sc != c_extern)
187 CTag::add(tok, typ, sc);
188
189 // Special rules for definitions at file scope
190 // ANSI 3.1.2.2 p. 22
191 switch (sc) {
192 case c_static:
193 tok.set_ec_attribute(is_cscope);
194 if ((id = Block::scope_block[Block::cu_block].obj.lookup(tok.get_name()))) {
195 if (id->get_type().get_storage_class() == c_unspecified)
196 /*
197 * @error
198 * An identifier is declared twice
199 * with compilation or linkage
200 * unit scope with conflicting
201 * declarations
202 */
203 Error::error(E_ERR, "conflicting declarations for identifier " + id->get_name());
204 Token::unify(id->get_token(), tok);
205 }
206 if (!typ.is_cfunction())
207 Fchar::get_fileid().metrics().add_fvar();
208 break;
209 case c_typedef:
210 tok.set_ec_attribute(is_cscope);
211 tok.set_ec_attribute(is_typedef);
212 break;
213 case c_enum:
214 tok.set_ec_attribute(is_cscope);
215 tok.set_ec_attribute(is_enumeration);
216 break;
217 default:
218 if (!typ.is_cfunction())
219 Fchar::get_fileid().metrics().add_pvar();
220 tok.set_ec_attribute(is_lscope);
221 break;
222 }
223 // A definition contributing data to the current CU
224 if (sc != c_extern && sc != c_typedef && sc != c_enum && !typ.is_cfunction()) {
225 if (DP())
226 Error::error(E_DEBUG, "Add provider through obj_define");
227 Fdep::add_provider(Fchar::get_fileid());
228 }
229 } else {
230 // Definitions at function block scope
231 if (sc != c_extern &&
232 Block::scope_block[Block::current_block].obj.lookup(tok.get_name())) {
233 /*
234 * @error
235 * An identifier is declared twice within the
236 * same block
237 */
238 Error::error(E_ERR, "Duplicate definition of identifier " + tok.get_name());
239 return;
240 }
241 }
242 // Locate/create the appropriate FCall object
243 FCall *fc = NULL;
244 if (typ.is_cfunction() && !typ.is_typedef()) {
245 if (DP())
246 cout << "Looking for function " << tok.get_name() << '\n';
247 tok.set_ec_attribute(is_cfunction);
248 if (sc == c_extern || (sc == c_unspecified && Block::current_block == Block::cu_block)) {
249 // Extern linkage: get it from the lu block which we do not normaly search
250 if ((id = Block::scope_block[Block::lu_block].obj.lookup(tok.get_name())) != NULL)
251 fc = id->get_fcall();
252 } else {
253 // Static linkage: get it from the normal blocks
254 if ((id = obj_lookup(tok.get_name())) != NULL)
255 fc = id->get_fcall();
256 }
257 // Try to match the function against one in another project
258 if (!fc) {
259 Token utok(tok.unique()); // To handle identical files
260 fc = dynamic_cast<FCall *>(Call::get_call(utok));
261 if (!fc) {
262 if (DP())
263 cout << "Creating new call\n";
264 fc = new FCall(utok, typ, tok.get_name());
265 }
266 }
267 fc->metrics().set_metric(FunMetrics::em_nparam, typ.get_nparam());
268 }
269
270 static Stab Block::*objptr = &Block::obj;
271 Block::define(objptr, tok, typ, fc);
272 /*
273 * Identifiers with extern scope are also added to the linkage unit
274 * definitions. These definitions are not searched, for locating objects,
275 * but are used for unification of objects, simulating the linking phase.
276 */
277 if (sc == c_extern || (sc == c_unspecified && Block::current_block == Block::cu_block)) {
278 GlobObj *go = NULL;
279 if ((id = Block::scope_block[Block::lu_block].obj.lookup(tok.get_name())) != NULL) {
280 Token::unify(id->get_token(), tok);
281 go = id->get_glob();
282 } else {
283 /*
284 * Create all the GlobObj elements we track at their declaration/definition point,
285 * so that id is correctly initialized with one. However, we add a def, only if it
286 * is a definition of a global variable, rather than a declaration.
287 */
288 if (!typ.is_cfunction()) {
289 if (DP())
290 cout << "Define global variable identifier " << tok.get_name() << endl;
291 // Try to match the global against one in another project
292 Token utok(tok.unique()); // To handle identical files
293 go = GlobObj::get_glob(utok);
294 if (!go) {
295 if (DP())
296 cout << "Creating new glob\n";
297 go = new GlobObj(utok, typ, tok.get_name());
298 }
299 }
300 id = Block::scope_block[Block::lu_block].obj.define(tok, typ, fc, go);
301 }
302 /*
303 * We test go, because it might be null if the object is defined as a function in one
304 * and as a variable in another.
305 */
306 if (go && !typ.is_cfunction() && sc == c_unspecified && Block::current_block == Block::cu_block)
307 go->add_def(Fchar::get_fileid());
308 }
309 }
310
311 /*
312 * Define the tok struct/union/enum tag to be of type typ
313 */
314 void
tag_define(const Token & tok,const Type & typ)315 tag_define(const Token& tok, const Type& typ)
316 {
317 // Store CTags info
318 CTag::add(tok, typ);
319 // For structures and unions (but not for enums) store their members
320 if (typ.is_su() && !typ.is_incomplete()) {
321 const vector <Id>& members = typ.get_members_by_ordinal();
322 for (vector <Id>::const_iterator i = members.begin(); i != members.end(); i++)
323 if (i->get_token().non_empty())
324 CTag::add(i->get_token(), typ.ctags_kind(), tok.get_name());
325 }
326
327 // Update symbol table
328 tok.set_ec_attribute(is_suetag);
329 static Stab Block::*tagptr = &Block::tag;
330 const Id *id;
331
332 if (DP())
333 cout << "Define tag [" << tok.get_name() << "]: " << typ << "\n";
334 if (Block::use_param && Block::current_block == Block::cu_block)
335 (Block::param_block.tag).define(tok, typ);
336 else if ((id = Block::scope_block[Block::current_block].tag.lookup(tok.get_name())) &&
337 !id->get_type().is_incomplete())
338 /*
339 * @error
340 * A structure, union, or enumeration tag was defined
341 * twice for the same entity
342 */
343 Error::error(E_ERR, "Duplicate definition of tag " + tok.get_name());
344 else
345 Block::define(tagptr, tok, typ);
346 }
347
348
349 /*
350 * Lookup in the block pointed by table (obj or tag) for name
351 * and return the relevant identifier or NULL if not defined
352 * and the definition's scope level.
353 */
354 pair <Id const *, int>
lookup(const Stab Block::* table,const string & name)355 Block::lookup(const Stab Block::*table, const string& name)
356 {
357 Id const * id;
358
359 for (int i = current_block; i != lu_block; i--)
360 if ((id = (scope_block[i].*table).lookup(name)))
361 return pair <Id const *, int>(id, i);
362 return pair <Id const *, int>(NULL, 0);
363 }
364
365 Id const *
lookup(const string & s) const366 Stab::lookup(const string& s) const
367 {
368 map<string, Id>::const_iterator i;
369
370 i = m.find(s);
371 if (i == m.end())
372 return (NULL);
373 else
374 return &((*i).second);
375 }
376
377
378 Id const *
obj_lookup(const string & name)379 obj_lookup(const string& name)
380 {
381 static Stab Block::*objptr = &Block::obj;
382 pair <Id const *, int> r = Block::lookup(objptr, name);
383 Id const *id = r.first;
384 if (id) {
385 enum e_storage_class sc = r.first->get_type().get_storage_class();
386 if (!r.first->get_type().is_cfunction() &&
387 (sc == c_extern || (sc == c_unspecified && r.second == Block::cu_block))) {
388 if (DP())
389 cout << "Lookup global variable identifier " << name << endl;
390 GlobObj *go = id->get_glob();
391 // Try to match the global against one in another project
392 if (!go) {
393 Token utok(id->get_token().unique()); // To handle identical files
394 go = GlobObj::get_glob(utok);
395 if (!go) {
396 if (DP())
397 cout << "Creating new glob\n";
398 go = new GlobObj(utok, id->get_type(), name);
399 }
400 }
401 go->add_ref(Fchar::get_fileid());
402 }
403 }
404 return id;
405 }
406
407
408 Id *
define(const Token & tok,const Type & typ,FCall * fc,GlobObj * go)409 Stab::define(const Token& tok, const Type& typ, FCall *fc, GlobObj *go)
410 {
411 Stab_element::key_type key(tok.get_name());
412 Stab_element::mapped_type data(Id(tok, typ, fc, go));
413 /*
414 * Efficient implementation of
415 * m[tok.get_name()] = Id(tok, typ, fc, go);
416 * return (Id *)Stab::lookup(tok.get_name());
417 * See Meyers Effective STL, Item 24.
418 */
419 Stab_element::iterator i = m.lower_bound(key);
420 if (i != m.end() && i->first == key) {
421 i->second = data;
422 return &(i->second);
423 } else
424 return &(m.insert(i, Stab_element::value_type(key, data))->second);
425 }
426
427 /*
428 * Define a local label (gcc extension)
429 */
430 void
local_label_define(const Token & tok)431 local_label_define(const Token& tok)
432 {
433 tok.set_ec_attribute(is_label);
434 static Stab Block::*llptr = &Block::local_label;
435 const Id *id;
436
437 if (DP())
438 cout << "Define local label [" << tok.get_name() << "\n";
439 if ((id = Block::scope_block[Block::current_block].local_label.lookup(tok.get_name())))
440 /*
441 * @error
442 * A local label was defined more than once in the same block
443 */
444 Error::error(E_ERR, "Duplicate local label definition " + tok.get_name());
445 else
446 Block::define(llptr, tok, Type());
447 }
448
449 /*
450 * Define tok as a label in the current function.
451 * If it is already defined, unify it with the previous definition.
452 * Locally defined labels take precedence
453 */
454 void
label_define(const Token & tok)455 label_define(const Token& tok)
456 {
457 tok.set_ec_attribute(is_label);
458 bool is_local;
459 static Stab Block::*llptr = &Block::local_label;
460
461 Id const *id;
462 // Search first for local, then for function label
463 if ((id = local_label_lookup(tok.get_name())))
464 is_local = true;
465 else {
466 id = Function::label.lookup(tok.get_name());
467 is_local = false;
468 }
469 if (id) {
470 if (id->get_type().is_valid())
471 /*
472 * @error
473 * The same <code>goto</code> label is defined more
474 * than once in a given function
475 */
476 Error::error(E_ERR, "label " + tok.get_name() + " already defined");
477 Token::unify(id->get_token(), tok);
478 }
479 if (is_local)
480 Block::define(llptr, tok, label());
481 else
482 Function::label.define(tok, label());
483 }
484
485 /*
486 * Use tok as a label in the current function (with goto).
487 * If it is already defined, unify it with the previous definition.
488 * Local labels take precedence in searching
489 */
490 void
label_use(const Token & tok)491 label_use(const Token& tok)
492 {
493 Id const *id;
494 if ((id = local_label_lookup(tok.get_name())) == NULL)
495 id = Function::label.lookup(tok.get_name());
496 if (id)
497 Token::unify(id->get_token(), tok);
498 else
499 Function::label.define(tok, Type());
500 }
501
502 // Called at the end of a function definition
503 void
exit()504 Function::exit()
505 {
506 Stab_element::const_iterator i;
507
508 for (i = label.begin(); i != label.end(); i++)
509 if (!Stab::get_id(i).get_type().is_valid())
510 /*
511 * @error
512 * A <code>goto</code>
513 * label used within a function was never defined
514 */
515 Error::error(E_ERR, "undefined label " + Stab::get_name(i));
516 label.clear();
517 }
518
519 ostream&
operator <<(ostream & o,const Stab & s)520 operator<<(ostream& o,const Stab &s)
521 {
522 Stab_element::const_iterator i;
523
524 o << "{";
525 for (i = s.m.begin(); i != s.m.end(); i++)
526 o << (*i).first << ": " << ((*i).second.get_type()) << "\n";
527 o << "} ";
528 return o;
529 }
530