summaryrefslogtreecommitdiffstats
path: root/Tools/c-analyzer/c_analyzer/variables/info.py
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/c-analyzer/c_analyzer/variables/info.py')
-rw-r--r--Tools/c-analyzer/c_analyzer/variables/info.py93
1 files changed, 93 insertions, 0 deletions
diff --git a/Tools/c-analyzer/c_analyzer/variables/info.py b/Tools/c-analyzer/c_analyzer/variables/info.py
new file mode 100644
index 0000000..336a523
--- /dev/null
+++ b/Tools/c-analyzer/c_analyzer/variables/info.py
@@ -0,0 +1,93 @@
+from collections import namedtuple
+
+from ..common.info import ID, UNKNOWN
+from ..common.util import classonly, _NTBase
+
+
+def normalize_vartype(vartype):
+ """Return the canonical form for a variable type (or func signature)."""
+ # We allow empty strring through for semantic reasons.
+ if vartype is None:
+ return None
+
+ # XXX finish!
+ # XXX Return (modifiers, type, pointer)?
+ return str(vartype)
+
+
+# XXX Variable.vartype -> decl (Declaration).
+
+class Variable(_NTBase,
+ namedtuple('Variable', 'id storage vartype')):
+ """Information about a single variable declaration."""
+
+ __slots__ = ()
+
+ STORAGE = (
+ 'static',
+ 'extern',
+ 'implicit',
+ 'local',
+ )
+
+ @classonly
+ def from_parts(cls, filename, funcname, name, decl, storage=None):
+ varid = ID(filename, funcname, name)
+ if storage is None:
+ self = cls.from_id(varid, decl)
+ else:
+ self = cls(varid, storage, decl)
+ return self
+
+ @classonly
+ def from_id(cls, varid, decl):
+ from ..parser.declarations import extract_storage
+ storage = extract_storage(decl, infunc=varid.funcname)
+ return cls(varid, storage, decl)
+
+ def __new__(cls, id, storage, vartype):
+ self = super().__new__(
+ cls,
+ id=ID.from_raw(id),
+ storage=str(storage) if storage else None,
+ vartype=normalize_vartype(vartype) if vartype else None,
+ )
+ return self
+
+ def __hash__(self):
+ return hash(self.id)
+
+ def __getattr__(self, name):
+ return getattr(self.id, name)
+
+ def _validate_id(self):
+ if not self.id:
+ raise TypeError('missing id')
+
+ if not self.filename or self.filename == UNKNOWN:
+ raise TypeError(f'id missing filename ({self.id})')
+
+ if self.funcname and self.funcname == UNKNOWN:
+ raise TypeError(f'id missing funcname ({self.id})')
+
+ self.id.validate()
+
+ def validate(self):
+ """Fail if the object is invalid (i.e. init with bad data)."""
+ self._validate_id()
+
+ if self.storage is None or self.storage == UNKNOWN:
+ raise TypeError('missing storage')
+ elif self.storage not in self.STORAGE:
+ raise ValueError(f'unsupported storage {self.storage:r}')
+
+ if self.vartype is None or self.vartype == UNKNOWN:
+ raise TypeError('missing vartype')
+
+ @property
+ def isglobal(self):
+ return self.storage != 'local'
+
+ @property
+ def isconst(self):
+ return 'const' in self.vartype.split()